-
Notifications
You must be signed in to change notification settings - Fork 657
Client Collateral Datas #2490
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: development
Are you sure you want to change the base?
Client Collateral Datas #2490
Changes from 3 commits
a15eb12
7bdd861
6bfac1e
276d8c8
696a1a5
6aa0bd3
a519ffd
756e103
ff97829
36beb86
9e47b4f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,182 @@ | ||
| package com.mifos.feature.loan.ClientCollateral | ||
|
|
||
|
|
||
|
|
||
| import androidclient.feature.client.generated.resources.Res | ||
| import androidclient.feature.client.generated.resources.client_product_shares_account | ||
| import androidclient.feature.client.generated.resources.client_savings_item | ||
| import androidclient.feature.client.generated.resources.filter | ||
| import androidclient.feature.client.generated.resources.search | ||
| import androidclient.feature.client.generated.resources.string_not_available | ||
| import androidclient.feature.loan.generated.resources.Res | ||
| import androidx.compose.foundation.layout.Column | ||
| import androidx.compose.foundation.layout.Row | ||
| import androidx.compose.foundation.layout.Spacer | ||
| import androidx.compose.foundation.layout.fillMaxSize | ||
| import androidx.compose.foundation.layout.fillMaxWidth | ||
| import androidx.compose.foundation.layout.height | ||
| import androidx.compose.foundation.layout.padding | ||
| import androidx.compose.foundation.lazy.LazyColumn | ||
| import androidx.compose.material3.Icon | ||
| import androidx.compose.material3.Text | ||
| import androidx.compose.runtime.Composable | ||
| import androidx.compose.runtime.getValue | ||
| import androidx.compose.runtime.remember | ||
| import androidx.compose.ui.Modifier | ||
| import androidx.lifecycle.compose.collectAsStateWithLifecycle | ||
| import androidx.navigation.NavController | ||
| import com.mifos.core.designsystem.component.MifosCircularProgress | ||
| import com.mifos.core.designsystem.component.MifosScaffold | ||
| import com.mifos.core.designsystem.component.MifosSweetError | ||
| import com.mifos.core.designsystem.theme.DesignToken | ||
| import com.mifos.core.designsystem.theme.MifosTypography | ||
| import com.mifos.core.designsystem.utils.onClick | ||
| import com.mifos.core.ui.components.Actions | ||
| import com.mifos.core.ui.components.MifosActionsCollateralDataListingComponent | ||
| import com.mifos.core.ui.components.MifosActionsShareListingComponent | ||
| import com.mifos.core.ui.components.MifosBreadcrumbNavBar | ||
| import com.mifos.core.ui.components.MifosEmptyCard | ||
| import com.mifos.core.ui.util.EventsEffect | ||
| import org.jetbrains.compose.resources.painterResource | ||
| import org.jetbrains.compose.resources.stringResource | ||
| import org.koin.compose.viewmodel.koinViewModel | ||
|
|
||
| @Composable | ||
| internal fun CollateralScreenRoute( | ||
| navController: NavController, | ||
| viewAccount: (Int) -> Unit, | ||
| viewModel: ClientCollateralViewmodel = koinViewModel(), | ||
| ) { | ||
| val state by viewModel.stateFlow.collectAsStateWithLifecycle() | ||
|
|
||
| EventsEffect(viewModel.eventFlow) { event -> | ||
| when (event) { | ||
| is collateralEvent.viewAccount -> viewAccount(event.accountsId) | ||
| } | ||
| } | ||
|
|
||
| collateralScreen( | ||
| state = state, | ||
| navController = navController, | ||
| onAction = remember(viewModel) { { viewModel.trySendAction(it) } }, | ||
| ) | ||
|
|
||
| ShareAccountsDialog( | ||
| state = state, | ||
| onAction = remember(viewModel) { { viewModel.trySendAction(it) } }, | ||
| ) | ||
| } | ||
|
|
||
| @Composable | ||
| internal fun collateralScreen( | ||
| navController: NavController, | ||
| state: collateralUiState, | ||
| onAction: (collateralAction) -> Unit, | ||
| ) { | ||
| MifosScaffold( | ||
| title = "Share Accounts", | ||
| onBackPressed = {}, | ||
| ) { paddingValues -> | ||
| Column( | ||
| modifier = Modifier.padding(paddingValues) | ||
| .fillMaxSize(), | ||
| ) { | ||
| MifosBreadcrumbNavBar( | ||
| navController = navController, | ||
| ) | ||
|
|
||
| when (state.isLoading) { | ||
| true -> MifosCircularProgress() | ||
|
|
||
| false -> { | ||
| Column( | ||
| modifier = Modifier.fillMaxSize() | ||
| .padding(horizontal = DesignToken.padding.large), | ||
| ) { | ||
| ShareAccountHeader( | ||
| totalItem = state.accounts.size.toString(), | ||
| onAction = onAction, | ||
| ) | ||
|
|
||
| Spacer(modifier = Modifier.height(DesignToken.padding.large)) | ||
|
|
||
| if (state.accounts.isNotEmpty()) { | ||
| val emptyText = stringResource(Res.string.string_not_available) | ||
|
|
||
| LazyColumn { | ||
| item { | ||
| state.accounts.forEachIndexed { index, account -> | ||
| MifosActionsCollateralDataListingComponent( | ||
| name = account.name ?: emptyText, | ||
| quantity = account.quantity?.toString() ?: emptyText, | ||
| totalValue = account.totalValue?.toString() ?: emptyText, | ||
| totalCollateralValue = account.totalCollateralValue?.toString() ?: emptyText, | ||
| ) | ||
|
|
||
| Spacer(Modifier.height(DesignToken.padding.small)) | ||
| } | ||
| } | ||
| } | ||
| } else { | ||
| MifosEmptyCard() | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| @Composable | ||
| private fun ShareAccountHeader( | ||
| totalItem: String, | ||
| onAction: (collateralAction) -> Unit, | ||
| ) { | ||
| Row( | ||
| modifier = Modifier.fillMaxWidth(), | ||
| ) { | ||
| Column { | ||
| Text( | ||
| text = stringResource(Res.string.client_product_shares_account), | ||
| style = MifosTypography.titleMedium, | ||
| ) | ||
|
|
||
| Text( | ||
| text = totalItem + " " + stringResource(Res.string.client_savings_item), | ||
| style = MifosTypography.labelMedium, | ||
| ) | ||
| } | ||
|
|
||
| Spacer(modifier = Modifier.weight(1f)) | ||
|
|
||
| // add a cross icon when its active, talk with design team | ||
| Icon( | ||
| modifier = Modifier.onClick { onAction.invoke(collateralAction.toggleSearchBar) }, | ||
| painter = painterResource(Res.drawable.search), | ||
| contentDescription = null, | ||
| ) | ||
|
|
||
| Icon( | ||
| modifier = Modifier.onClick { onAction.invoke(collateralAction.toggleFiler) }, | ||
| painter = painterResource(Res.drawable.filter), | ||
| contentDescription = null, | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| @Composable | ||
| private fun ShareAccountsDialog( | ||
| state: collateralUiState, | ||
| onAction: (collateralAction) -> Unit, | ||
| ) { | ||
| when (state.dialogState) { | ||
| is collateralUiState.DialogState.Error -> { | ||
| MifosSweetError( | ||
| message = state.dialogState.message, | ||
| onclick = { onAction.invoke(collateralAction.refresh) }, | ||
| ) | ||
| } | ||
|
|
||
| null -> {} | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,127 @@ | ||
| package com.mifos.feature.loan.ClientCollateral | ||
|
|
||
| import androidx.lifecycle.SavedStateHandle | ||
| import androidx.lifecycle.viewModelScope | ||
| import androidx.navigation.toRoute | ||
| import com.mifos.core.data.repository.ClientDetailsRepository | ||
| import com.mifos.core.ui.util.BaseViewModel | ||
|
|
||
| import com.mifos.core.network.model.CollateralItem | ||
| import com.mifos.feature.loan.Clientcollateral.clientCollateralRoute | ||
|
|
||
| import kotlinx.coroutines.flow.update | ||
| import kotlinx.coroutines.launch | ||
|
|
||
|
|
||
| class ClientCollateralViewmodel ( | ||
| savedStateHandle: SavedStateHandle, | ||
| private val repository: ClientDetailsRepository | ||
| ): BaseViewModel<collateralUiState,collateralEvent,collateralAction>( | ||
| initialState = collateralUiState() | ||
| ){ | ||
| private val route = savedStateHandle.toRoute<clientCollateralRoute>() | ||
| override fun handleAction(action: collateralAction) { | ||
| when (action) { | ||
| is collateralAction.cardClicked -> handleCardClicked(action.activeIndex) | ||
| collateralAction.toggleFiler -> toggleFiler() | ||
| collateralAction.toggleSearchBar -> toggleSearchBar() | ||
| is collateralAction.viewAccount -> sendEvent(collateralEvent.viewAccount(action.accountId)) | ||
| collateralAction.refresh -> fetchAllCollateralAccount() | ||
|
|
||
| } | ||
|
|
||
| } | ||
|
||
| init{ | ||
| fetchAllCollateralAccount() | ||
| } | ||
| private fun fetchAllCollateralAccount(){ | ||
| viewModelScope.launch { | ||
| mutableStateFlow.update{ | ||
| it.copy( | ||
| isLoading = true, | ||
| ) | ||
|
|
||
| } | ||
|
||
| try{ | ||
| val result = repository.getCollateralItems(route.clientId) | ||
| mutableStateFlow.update { | ||
| it.copy( | ||
| isLoading = false, | ||
| accounts = result as List<CollateralItem>, | ||
| dialogState = null | ||
|
|
||
|
|
||
| ) | ||
| } | ||
| }catch(e : Exception){ | ||
|
||
| mutableStateFlow.update{ | ||
| it.copy( | ||
| isLoading = false, | ||
| dialogState = collateralUiState.DialogState.Error(e.message ?: "Unknown Error") | ||
| ) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| } | ||
| private fun toggleFiler() { | ||
| mutableStateFlow.update { | ||
| it.copy( | ||
| isFilterActive = !state.isFilterActive, | ||
| ) | ||
| } | ||
|
|
||
|
|
||
| } | ||
| private fun toggleSearchBar() { | ||
| mutableStateFlow.update { | ||
| it.copy( | ||
| isSearchBarActive = !state.isSearchBarActive, | ||
| ) | ||
| } | ||
|
|
||
| } | ||
|
||
| private fun handleCardClicked(index : Int){ | ||
| mutableStateFlow.update { | ||
| it.copy( | ||
| isCardActive = !state.isCardActive, | ||
| currentlyActiveIndex = index, | ||
| ) | ||
| } | ||
|
|
||
| } | ||
|
|
||
|
|
||
|
||
| } | ||
|
|
||
| data class collateralUiState( | ||
| val isLoading : Boolean = false, | ||
| val isFilterActive : Boolean = false, | ||
| val accounts : List<CollateralItem> = emptyList(), | ||
| val isSearchBarActive :Boolean = false, | ||
| val isCardActive : Boolean = false, | ||
| val currentlyActiveIndex: Int = -1, | ||
| val dialogState : DialogState? = null,){ | ||
|
|
||
| sealed interface DialogState{ | ||
| data class Error (val message : String) : DialogState | ||
| } | ||
|
|
||
|
|
||
|
|
||
|
|
||
| } | ||
| sealed interface collateralEvent{ | ||
| data class viewAccount ( val accountsId : Int) : collateralEvent | ||
|
|
||
| } | ||
| sealed interface collateralAction{ | ||
| data object toggleFiler : collateralAction | ||
| data object toggleSearchBar : collateralAction | ||
| data class cardClicked (val activeIndex : Int ): collateralAction | ||
| data class viewAccount (val accountId : Int) : collateralAction | ||
| data object refresh : collateralAction | ||
|
|
||
|
|
||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| package com.mifos.feature.loan.Clientcollateral | ||
|
|
||
|
|
||
| import androidx.navigation.NavController | ||
| import androidx.navigation.NavGraphBuilder | ||
| import androidx.navigation.compose.composable | ||
| import com.mifos.feature.loan.ClientCollateral.CollateralScreenRoute | ||
|
|
||
| import kotlinx.serialization.Serializable | ||
|
|
||
| @Serializable | ||
| data class clientCollateralRoute( | ||
| val clientId : Int = -1, | ||
| ) | ||
| fun NavGraphBuilder.clientCollateralDestination( | ||
| navController: NavController, | ||
| navigateToViewAccount: (Int) -> Unit, | ||
| ) { | ||
| composable<clientCollateralRoute> { | ||
| CollateralScreenRoute( | ||
| navController = navController, | ||
| viewAccount = navigateToViewAccount, | ||
| ) | ||
| } | ||
|
|
||
| } | ||
| fun NavController.navigatetoCollateralScreen( | ||
| clientId : Int, | ||
| ){ | ||
| this.navigate( | ||
| clientCollateralRoute( | ||
| clientId = clientId, | ||
| ) | ||
|
|
||
|
|
||
| ) | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You needed help with navigation right.
So, in this project we are using type safe navigation and also nested nav graphs.
I will give you a simple analogy.
Consider a Navgraph like your house (collection of multiple rooms). All houses have a door to enter inside, (now don't think what about windows or balcony), and it always opens inside one room always. Just like that a navgraph is a collection of multiple navigation destinations (screen). When you navigate to a navgraph there is always a screen set a startdestination that opens first..
And just like from inside of your room you can go inside multiple other room, so, just like that you can go to multiple other screens from that screen.
You won't create a navgraph here.
Learn about typesafe navigation and then see how we are using it.
In typesafe navigation you use serialized data classes instead of string route like in web or in normal string based navigation.
Here is an example:
Instead of using a string you will use such data classes. The arguments you pass along with string routs are instead passed a parameters to the data class.
Also learn about extension functions.
You will create a navigation destination(route) by creating a extension function on the NavGraphBuilder, something like this
NavGraphBuilder.navigateToCleintCollateralRoute
Just look into the client profile screen and see how navigation is done there.
If you need help ask.