diff --git a/feature/client/src/androidMain/kotlin/com/mifos/feature/client/clientsList/ClientListScreen.android.kt b/feature/client/src/androidMain/kotlin/com/mifos/feature/client/clientsList/ClientListScreen.android.kt index 70ca19fa561..fe38df6efab 100644 --- a/feature/client/src/androidMain/kotlin/com/mifos/feature/client/clientsList/ClientListScreen.android.kt +++ b/feature/client/src/androidMain/kotlin/com/mifos/feature/client/clientsList/ClientListScreen.android.kt @@ -16,6 +16,7 @@ import androidclient.feature.client.generated.resources.feature_client_no_more_c import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -41,8 +42,18 @@ internal actual fun LazyColumnForClientListApi( fetchImage: (Int) -> Unit, images: Map, modifier: Modifier, + sort: SortTypes?, + onUpdateOffices: (List) -> Unit, ) { val clientPagingList = pagingFlow.collectAsLazyPagingItems() + + val items = clientPagingList.itemSnapshotList.items + if (items.isNotEmpty()) { + val offices = items.map { it.officeName } + .distinct() + onUpdateOffices(offices) + } + when (clientPagingList.loadState.refresh) { is LoadState.Error -> { MifosSweetError(message = stringResource(Res.string.feature_client_failed_to_fetch_clients)) { @@ -55,14 +66,29 @@ internal actual fun LazyColumnForClientListApi( is LoadState.NotLoading -> Unit } - LazyColumn( - modifier = modifier, - ) { - items( - count = clientPagingList.itemCount, - key = { index -> clientPagingList[index]?.id ?: index }, - ) { index -> - clientPagingList[index]?.let { client -> + if (sort != null) { + val currentItems = clientPagingList.itemSnapshotList.items + + val sortedItems = when (sort) { + SortTypes.NAME -> { + currentItems.sortedBy { it.displayName?.lowercase() } + } + SortTypes.ACCOUNT_NUMBER -> { + currentItems.sortedBy { it.accountNo } + } + SortTypes.EXTERNAL_ID -> { + currentItems.sortedBy { it.externalId } + } + else -> currentItems + } + + LazyColumn( + modifier = modifier, + ) { + items( + items = sortedItems, + key = { client -> client.id }, + ) { client -> LaunchedEffect(client.id) { fetchImage(client.id) } @@ -73,36 +99,56 @@ internal actual fun LazyColumnForClientListApi( ) } } - - when (clientPagingList.loadState.append) { - is LoadState.Error -> { - item { - MifosSweetError(message = stringResource(Res.string.feature_client_failed_to_more_clients)) { - onRefresh() + } else { + LazyColumn( + modifier = modifier, + ) { + items( + count = clientPagingList.itemCount, + key = { index -> clientPagingList[index]?.id ?: index }, + ) { index -> + clientPagingList[index]?.let { client -> + LaunchedEffect(client.id) { + fetchImage(client.id) } + ClientItem( + client = client, + byteArray = images[client.id], + onClientClick = onClientSelect, + ) } } - is LoadState.Loading -> { - item { - MifosPagingAppendProgress() + when (clientPagingList.loadState.append) { + is LoadState.Error -> { + item { + MifosSweetError(message = stringResource(Res.string.feature_client_failed_to_more_clients)) { + onRefresh() + } + } } - } - is LoadState.NotLoading -> { - if (clientPagingList.loadState.append.endOfPaginationReached && - clientPagingList.itemCount > 0 - ) { + is LoadState.Loading -> { item { - Text( - modifier = Modifier - .fillMaxWidth() - .padding(bottom = DesignToken.padding.extraExtraLarge) - .padding(bottom = DesignToken.padding.extraExtraLarge), - text = stringResource(Res.string.feature_client_no_more_clients_available), - style = MifosTypography.bodyMedium, - textAlign = TextAlign.Center, - ) + MifosPagingAppendProgress() + } + } + + is LoadState.NotLoading -> { + if (clientPagingList.loadState.append.endOfPaginationReached && + clientPagingList.itemCount > 0 + ) { + item { + Text( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = DesignToken.padding.extraExtraLarge) + .padding(bottom = DesignToken.padding.extraExtraLarge), + text = stringResource(Res.string.feature_client_no_more_clients_available), + style = MifosTypography.bodyMedium, + textAlign = TextAlign.Center, + ) + } } } } diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientsList/ClientListScreen.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientsList/ClientListScreen.kt index fd1df7659da..0ac5b9e33c5 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientsList/ClientListScreen.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientsList/ClientListScreen.kt @@ -12,6 +12,7 @@ package com.mifos.feature.client.clientsList import androidclient.feature.client.generated.resources.Res import androidclient.feature.client.generated.resources.account_number_prefix import androidclient.feature.client.generated.resources.string_not_available +import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -24,15 +25,26 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.material3.Checkbox +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.RadioButton +import androidx.compose.material3.SheetState import androidx.compose.material3.Text +import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.paging.PagingData import com.mifos.core.designsystem.component.BasicDialogState @@ -51,6 +63,7 @@ import kotlinx.coroutines.flow.Flow import org.jetbrains.compose.resources.stringResource import org.koin.compose.viewmodel.koinViewModel +@OptIn(ExperimentalMaterial3Api::class) @Composable internal fun ClientListScreen( createNewClient: () -> Unit, @@ -58,8 +71,25 @@ internal fun ClientListScreen( modifier: Modifier = Modifier, viewModel: ClientListViewModel = koinViewModel(), ) { + val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) val state by viewModel.stateFlow.collectAsStateWithLifecycle() + if (state.isFilterVisible) { + FilterBottomSheet( + onDismissRequest = { viewModel.trySendAction(ClientListAction.ToggleFilterVisibility) }, + sheetState = sheetState, + handleFilterClick = { value, filterType -> + viewModel.trySendAction(ClientListAction.HandleFilterClick(value, filterType)) + }, + selectedStatuses = state.selectedStatus, + selectedSort = state.sort, + handleSortClick = { viewModel.trySendAction(ClientListAction.HandleSortClick(it)) }, + officeNames = state.officeNames, + selectedOffices = state.selectedOffices, + clearFilters = { viewModel.trySendAction(ClientListAction.ClearFilters) }, + ) + } + EventsEffect(viewModel.eventFlow) { event -> when (event) { is ClientListEvent.OnClientClick -> onClientClick(event.clientId) @@ -71,6 +101,8 @@ internal fun ClientListScreen( modifier = modifier, state = state, onAction = remember(viewModel) { { viewModel.trySendAction(it) } }, + toggleFilterVisibility = { viewModel.trySendAction(ClientListAction.ToggleFilterVisibility) }, + onUpdateOffices = { viewModel.trySendAction(ClientListAction.OnUpdateOffice(it)) }, ) ClientListDialogs( @@ -86,6 +118,7 @@ private fun ClientActions( state: ClientListState, onAction: (ClientListAction) -> Unit, modifier: Modifier = Modifier, + toggleFilterVisibility: () -> Unit, ) { Row( modifier = modifier.fillMaxWidth().padding(DesignToken.padding.large), @@ -143,14 +176,15 @@ private fun ClientActions( // } } Spacer(Modifier.width(DesignToken.padding.largeIncreased)) -// Icon( -// imageVector = MifosIcons.Filter, -// contentDescription = null, -// modifier = Modifier -// .size(DesignToken.sizes.iconAverage) -// .clickable { -// }, -// ) + Icon( + imageVector = MifosIcons.Filter, + contentDescription = null, + modifier = Modifier + .size(DesignToken.sizes.iconAverage) + .clickable { + toggleFilterVisibility() + }, + ) } } @@ -159,47 +193,55 @@ private fun ClientListContentScreen( state: ClientListState, modifier: Modifier = Modifier, onAction: (ClientListAction) -> Unit, + toggleFilterVisibility: () -> Unit, + onUpdateOffices: (List) -> Unit, ) { - if (state.isEmpty) { - MifosEmptyCard("No clients found") - } - if (state.clients.isNotEmpty()) { - ClientListContent( - clientsList = state.clients, - onClientClick = { clientId -> - onAction(ClientListAction.OnClientClick(clientId)) - }, - modifier = modifier.padding(DesignToken.padding.large), - fetchImage = { - onAction(ClientListAction.FetchImage(it)) - }, - images = state.clientImages, - ) - } - if (state.clientsFlow != null) { - Column( - Modifier.fillMaxSize(), - ) { - if (state.dialogState == null) { - ClientActions( - state = state, - onAction = onAction, + Column( + modifier = Modifier.fillMaxSize(), + ) { + if (!state.isEmpty) { + ClientActions( + state = state, + onAction = onAction, + toggleFilterVisibility = toggleFilterVisibility, + ) + } + + when { + state.clients.isNotEmpty() -> { + ClientListContent( + clientsList = state.clients, + onClientClick = { clientId -> + onAction(ClientListAction.OnClientClick(clientId)) + }, + modifier = modifier.padding(DesignToken.padding.large), + fetchImage = { + onAction(ClientListAction.FetchImage(it)) + }, + images = state.clientImages, ) } - LazyColumnForClientListApi( - pagingFlow = state.clientsFlow, - onRefresh = { - onAction(ClientListAction.RefreshClients) - }, - onClientSelect = { - onAction(ClientListAction.OnClientClick(it)) - }, - modifier = Modifier, - fetchImage = { - onAction(ClientListAction.FetchImage(it)) - }, - images = state.clientImages, - ) + state.clientsFlow != null -> { + LazyColumnForClientListApi( + pagingFlow = state.clientsFlow, + onRefresh = { + onAction(ClientListAction.RefreshClients) + }, + onClientSelect = { + onAction(ClientListAction.OnClientClick(it)) + }, + modifier = Modifier, + fetchImage = { + onAction(ClientListAction.FetchImage(it)) + }, + images = state.clientImages, + sort = state.sort, + onUpdateOffices = onUpdateOffices, + ) + } + else -> { + MifosEmptyCard("No clients found") + } } } } @@ -310,4 +352,219 @@ internal expect fun LazyColumnForClientListApi( fetchImage: (Int) -> Unit, images: Map, modifier: Modifier = Modifier, + sort: SortTypes?, + onUpdateOffices: (List) -> Unit, ) + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun FilterBottomSheet( + onDismissRequest: () -> Unit, + sheetState: SheetState, + handleFilterClick: (String, FilterType) -> Unit, + selectedStatuses: List, + selectedSort: SortTypes?, + handleSortClick: (SortTypes) -> Unit, + officeNames: List, + selectedOffices: List, + clearFilters: () -> Unit, +) { + ModalBottomSheet( + onDismissRequest = onDismissRequest, + sheetState = sheetState, + dragHandle = null, + containerColor = MaterialTheme.colorScheme.background, + ) { + val sortTypes = listOf(SortTypes.NAME, SortTypes.ACCOUNT_NUMBER, SortTypes.EXTERNAL_ID) + val statusTypes = listOf("Active", "Pending", "Closed") + + Column( + modifier = Modifier.padding(15.dp), + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.fillMaxWidth() + .padding(10.dp), + ) { + Text( + text = "Filters", + style = MifosTypography.titleLargeEmphasized, + color = MaterialTheme.colorScheme.primary, + ) + Row { + IconButton( + onClick = { + clearFilters() + onDismissRequest() + }, + ) { + Icon( + imageVector = MifosIcons.Redo, + contentDescription = "Clear", + ) + } + IconButton( + onClick = onDismissRequest, + ) { + Icon( + imageVector = MifosIcons.Check, + contentDescription = "Apply", + ) + } + } + } + HorizontalDivider(Modifier.fillMaxWidth(), thickness = 1.5.dp) + Column( + modifier = Modifier.padding(10.dp), + ) { + var isExpanded by remember { mutableStateOf(false) } + Row( + modifier = Modifier.fillMaxWidth() + .clickable(onClick = { + isExpanded = !isExpanded + }), + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Text( + text = "Sort by", + style = MifosTypography.titleMediumEmphasized, + ) + if (isExpanded) { + Icon( + imageVector = MifosIcons.ArrowDropUp, + contentDescription = "", + ) + } else { + Icon( + imageVector = MifosIcons.ArrowDropDown, + contentDescription = "", + ) + } + } + AnimatedVisibility( + visible = isExpanded, + ) { + Column { + sortTypes.forEach { sort -> + val isSelected = (sort == selectedSort) + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + Spacer(modifier = Modifier.width(10.dp)) + RadioButton( + selected = isSelected, + onClick = { + handleSortClick(sort) + }, + ) + Text(text = sort.value) + } + } + } + } + } + HorizontalDivider(Modifier.fillMaxWidth(), thickness = 1.5.dp) + Column( + modifier = Modifier.padding(10.dp), + ) { + var isExpanded by remember { mutableStateOf(false) } + Row( + modifier = Modifier.fillMaxWidth() + .clickable(onClick = { + isExpanded = !isExpanded + }), + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Text( + text = "Account Status", + style = MifosTypography.titleMediumEmphasized, + ) + if (isExpanded) { + Icon( + imageVector = MifosIcons.ArrowDropUp, + contentDescription = "", + ) + } else { + Icon( + imageVector = MifosIcons.ArrowDropDown, + contentDescription = "", + ) + } + } + AnimatedVisibility( + visible = isExpanded, + ) { + Column { + statusTypes.forEach { status -> + val isChecked = selectedStatuses.contains(status) + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + Spacer(modifier = Modifier.width(10.dp)) + Checkbox( + checked = isChecked, + onCheckedChange = { handleFilterClick(status, FilterType.STATUS) }, + ) + Text(text = status) + } + } + } + } + } + HorizontalDivider(Modifier.fillMaxWidth(), thickness = 1.5.dp) + + Column( + modifier = Modifier.padding(10.dp), + ) { + var isExpanded by remember { mutableStateOf(false) } + Row( + modifier = Modifier.fillMaxWidth() + .clickable(onClick = { + isExpanded = !isExpanded + }), + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Text( + "Office Name", + style = MifosTypography.titleMediumEmphasized, + ) + if (isExpanded) { + Icon( + imageVector = MifosIcons.ArrowDropUp, + contentDescription = "", + ) + } else { + Icon( + imageVector = MifosIcons.ArrowDropDown, + contentDescription = "", + ) + } + } + + AnimatedVisibility( + visible = isExpanded, + ) { + Column { + officeNames.forEach { name -> + val isChecked = selectedOffices.contains(name) + if (name != null) { + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + Spacer(modifier = Modifier.width(10.dp)) + Checkbox( + checked = isChecked, + onCheckedChange = { handleFilterClick(name, FilterType.OFFICE) }, + ) + Text(text = name) + } + } + } + } + } + } + HorizontalDivider(Modifier.fillMaxWidth(), thickness = 1.5.dp) + } + } +} diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientsList/ClientListViewModel.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientsList/ClientListViewModel.kt index f31365f56e4..7ca55c40bef 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientsList/ClientListViewModel.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientsList/ClientListViewModel.kt @@ -13,6 +13,7 @@ import androidclient.feature.client.generated.resources.Res import androidclient.feature.client.generated.resources.feature_client_failed_to_load_client import androidx.lifecycle.viewModelScope import androidx.paging.PagingData +import androidx.paging.filter import com.mifos.core.common.utils.DataState import com.mifos.core.common.utils.Page import com.mifos.core.data.repository.ClientDetailsRepository @@ -22,7 +23,9 @@ import com.mifos.core.ui.util.BaseViewModel import com.mifos.core.ui.util.imageToByteArray import com.mifos.room.entities.client.ClientEntity import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch @@ -36,6 +39,7 @@ internal class ClientListViewModel( clients = emptyList(), isOnline = false, clientsFlow = null, + unfilteredClients = emptyList(), ), ) { @@ -74,6 +78,12 @@ internal class ClientListViewModel( ) } } + + ClientListAction.ToggleFilterVisibility -> toggleFilterVisibility() + is ClientListAction.HandleSortClick -> handleSortClick(action.sort) + is ClientListAction.OnUpdateOffice -> onUpdateOffice(action.offices) + is ClientListAction.HandleFilterClick -> handleFilterClick(action.filter, action.filterType) + ClientListAction.ClearFilters -> clearFilters() } } @@ -144,7 +154,7 @@ internal class ClientListViewModel( if (data.isEmpty()) { it.copy(isEmpty = true, dialogState = null) } else { - it.copy(clients = data, dialogState = null) + it.copy(clients = data, dialogState = null, unfilteredClients = data) } } } @@ -155,6 +165,7 @@ internal class ClientListViewModel( state.copy( clientsFlow = result, dialogState = null, + unfilteredClientsFlow = result, ) } } @@ -177,6 +188,95 @@ internal class ClientListViewModel( } } } + + private fun handleSortClick(sort: SortTypes?) { + updateState { + val sortedList = when (sort) { + SortTypes.NAME -> it.clients.sortedBy { it.displayName?.lowercase() } + SortTypes.ACCOUNT_NUMBER -> it.clients.sortedBy { it.accountNo } + SortTypes.EXTERNAL_ID -> it.clients.sortedBy { it.externalId } + else -> it.clients + } + + it.copy( + sort = sort, + clients = sortedList, + ) + } + } + + private fun toggleFilterVisibility() { + updateState { + it.copy( + isFilterVisible = !it.isFilterVisible, + ) + } + } + + private fun onUpdateOffice(offices: List) { + updateState { + it.copy( + officeNames = (offices + it.officeNames).distinct().sortedBy { it }, + ) + } + } + + private fun handleFilterClick(filter: String, filterType: FilterType) { + updateState { + val newSelectedStatus = if (filterType == FilterType.STATUS) { + if (filter in it.selectedStatus) { + it.selectedStatus - filter + } else { + it.selectedStatus + filter + } + } else { + it.selectedStatus + } + val newSelectedOffices = if (filterType == FilterType.OFFICE) { + if (filter in it.selectedOffices) { + it.selectedOffices - filter + } else { + it.selectedOffices + filter + } + } else { + it.selectedOffices + } + + fun keep(client: ClientEntity): Boolean { + val statusMatch = newSelectedStatus.isEmpty() || client.status?.value in newSelectedStatus + val officeMatch = newSelectedOffices.isEmpty() || (client.officeName ?: "Null") in newSelectedOffices + + return statusMatch && officeMatch + } + val filteredList = it.unfilteredClients.filter { client -> + keep(client) + } + + val filteredFlow = it.unfilteredClientsFlow?.map { clients -> + clients.filter { client -> + keep(client) + } + } + it.copy( + selectedStatus = newSelectedStatus, + selectedOffices = newSelectedOffices, + clients = filteredList, + clientsFlow = filteredFlow, + ) + } + } + + private fun clearFilters() { + updateState { + it.copy( + clients = it.unfilteredClients, + clientsFlow = it.unfilteredClientsFlow, + sort = null, + selectedStatus = emptyList(), + selectedOffices = emptyList(), + ) + } + } } /** @@ -184,13 +284,20 @@ internal class ClientListViewModel( */ data class ClientListState( val clients: List, + val unfilteredClients: List, val clientsFlow: Flow>?, + val unfilteredClientsFlow: Flow>? = null, val isOnline: Boolean, val isEmpty: Boolean = false, val isSearchActive: Boolean = false, val dialogState: DialogState? = null, val searchQuery: String = "", val clientImages: Map = emptyMap(), + val sort: SortTypes? = null, + val selectedStatus: List = emptyList(), + val isFilterVisible: Boolean = false, + val officeNames: List = emptyList(), + val selectedOffices: List = emptyList(), ) { sealed interface DialogState { data class Error(val message: String) : DialogState @@ -198,6 +305,17 @@ data class ClientListState( } } +enum class SortTypes(val value: String) { + NAME("Name"), + ACCOUNT_NUMBER("Account Number"), + EXTERNAL_ID("External ID"), +} + +enum class FilterType(val value: String) { + STATUS("Status"), + OFFICE("Office"), +} + /** * UI events for the Client List screen. */ @@ -218,6 +336,11 @@ sealed interface ClientListAction { data object DismissSearch : ClientListAction data object NavigateToCreateClient : ClientListAction data class OnQueryChange(val query: String) : ClientListAction + data object ToggleFilterVisibility : ClientListAction + data class HandleFilterClick(val filter: String, val filterType: FilterType) : ClientListAction + data class HandleSortClick(val sort: SortTypes) : ClientListAction + data class OnUpdateOffice(val offices: List) : ClientListAction + data object ClearFilters : ClientListAction sealed class Internal : ClientListAction { data class ReceiveClientResult(val result: Flow>) : Internal() diff --git a/feature/client/src/desktopMain/kotlin/com/mifos/feature/client/clientsList/ClientListScreen.desktop.kt b/feature/client/src/desktopMain/kotlin/com/mifos/feature/client/clientsList/ClientListScreen.desktop.kt index ccb46e1edf3..e35fe896b97 100644 --- a/feature/client/src/desktopMain/kotlin/com/mifos/feature/client/clientsList/ClientListScreen.desktop.kt +++ b/feature/client/src/desktopMain/kotlin/com/mifos/feature/client/clientsList/ClientListScreen.desktop.kt @@ -23,5 +23,7 @@ internal actual fun LazyColumnForClientListApi( fetchImage: (Int) -> Unit, images: Map, modifier: Modifier, + sort: SortTypes?, + onUpdateOffices: (List) -> Unit, ) { } diff --git a/feature/client/src/iosMain/kotlin/com/mifos/feature/client/clientsList/ClientListScreen.ios.kt b/feature/client/src/iosMain/kotlin/com/mifos/feature/client/clientsList/ClientListScreen.ios.kt new file mode 100644 index 00000000000..e35fe896b97 --- /dev/null +++ b/feature/client/src/iosMain/kotlin/com/mifos/feature/client/clientsList/ClientListScreen.ios.kt @@ -0,0 +1,29 @@ +/* + * 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/android-client/blob/master/LICENSE.md + */ +package com.mifos.feature.client.clientsList + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.paging.PagingData +import com.mifos.room.entities.client.ClientEntity +import kotlinx.coroutines.flow.Flow + +@Composable +internal actual fun LazyColumnForClientListApi( + pagingFlow: Flow>, + onRefresh: () -> Unit, + onClientSelect: (Int) -> Unit, + fetchImage: (Int) -> Unit, + images: Map, + modifier: Modifier, + sort: SortTypes?, + onUpdateOffices: (List) -> Unit, +) { +}