diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_account/SavingAccountsTransactionFragment.kt b/app/src/main/java/org/mifos/mobile/ui/savings_account/SavingAccountsTransactionFragment.kt index 71f05b2f6..20d82f583 100644 --- a/app/src/main/java/org/mifos/mobile/ui/savings_account/SavingAccountsTransactionFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/savings_account/SavingAccountsTransactionFragment.kt @@ -1,4 +1,8 @@ +<<<<<<<< HEAD:app/src/main/java/org/mifos/mobile/ui/savings_account/SavingAccountsTransactionFragment.kt package org.mifos.mobile.ui.savings_account +======== +package org.mifos.mobile.ui.savings_account_transaction +>>>>>>>> cbc8d5af (A):app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountsTransactionFragment.kt import android.content.Intent import android.net.Uri @@ -42,7 +46,10 @@ import org.mifos.mobile.core.common.utils.ParcelableAndSerializableUtils.getChec import org.mifos.mobile.utils.StatusUtils import org.mifos.mobile.utils.Toaster import org.mifos.mobile.core.common.utils.getDatePickerDialog +<<<<<<<< HEAD:app/src/main/java/org/mifos/mobile/ui/savings_account/SavingAccountsTransactionFragment.kt import org.mifos.mobile.feature.savings.savings_account_transaction.SavingAccountsTransactionViewModel +======== +>>>>>>>> cbc8d5af (A):app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountsTransactionFragment.kt import java.time.Instant import javax.inject.Inject diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountTransactionContent.kt b/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountTransactionContent.kt new file mode 100644 index 000000000..a5316f33e --- /dev/null +++ b/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountTransactionContent.kt @@ -0,0 +1,154 @@ +package org.mifos.mobile.ui.savings_account_transaction + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +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.padding +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.HorizontalDivider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import org.mifos.mobile.R +import org.mifos.mobile.core.model.entity.accounts.savings.Transactions +import org.mifos.mobile.core.ui.theme.MifosMobileTheme +import org.mifos.mobile.core.common.utils.CurrencyUtil +import org.mifos.mobile.core.common.utils.DateHelper + +@Composable +fun SavingsAccountTransactionContent( + transactionList: List, +) { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.SpaceBetween + ) { + LazyColumn { + items(items = transactionList) { + SavingsAccountTransactionListItem(it) + HorizontalDivider( + thickness = 1.dp, + color = Color.Gray, + modifier = Modifier.padding(vertical = 4.dp) + ) + } + } + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp, horizontal = 10.dp), + horizontalArrangement = Arrangement.spacedBy(4.dp), + verticalAlignment = Alignment.Bottom + ) { + Text( + text = stringResource(id = R.string.need_help), + color = MaterialTheme.colorScheme.onSurface + ) + Spacer(modifier = Modifier.width(2.dp)) + Text( + text = stringResource(id = R.string.help_line_number), + color = MaterialTheme.colorScheme.primary + ) + } + } +} + + +@Composable +fun SavingsAccountTransactionListItem(transaction: Transactions) { + val context = LocalContext.current + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 6.dp) + ) { + Image( + painter = painterResource( + id = getTransactionTriangleResId(transaction.transactionType) + ), + contentDescription = stringResource(id = R.string.savings_account_transaction), + modifier = Modifier + .size(56.dp) + .padding(4.dp) + ) + Column( + modifier = Modifier.padding(4.dp) + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Text( + text = DateHelper.getDateAsString(transaction.date), + style = MaterialTheme.typography.labelLarge, + color = MaterialTheme.colorScheme.onSurface + ) + Text( + text = stringResource( + id = R.string.string_and_string, + transaction.currency?.displaySymbol ?: transaction.currency?.code ?: "", + CurrencyUtil.formatCurrency(context, transaction.amount,) + ), + style = MaterialTheme.typography.labelLarge, + color = MaterialTheme.colorScheme.onSurface + ) + } + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = transaction.transactionType?.value ?: "", + style = MaterialTheme.typography.labelMedium, + modifier = Modifier.alpha(0.7f), + color = MaterialTheme.colorScheme.onSurface + ) + Text( + text = stringResource( + id = R.string.string_and_string, + transaction.currency?.displaySymbol ?: transaction.currency?.code ?: "", + CurrencyUtil.formatCurrency(context, transaction.runningBalance) + ), + style = MaterialTheme.typography.labelMedium, + modifier = Modifier.alpha(0.7f), + color = MaterialTheme.colorScheme.onSurface + ) + } + Row( + modifier = Modifier.fillMaxWidth() + ) { + Text( + text = transaction.paymentDetailData?.paymentType?.name.toString(), + style = MaterialTheme.typography.labelMedium, + modifier = Modifier.alpha(0.7f), + color = MaterialTheme.colorScheme.onSurface + ) + } + } + } +} + +@Preview +@Composable +fun SavingsAccountTransactionContentPreview() { + MifosMobileTheme { + SavingsAccountTransactionContent(transactionList = listOf()) + } +} \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountsTransactionFilterDialog.kt b/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountsTransactionFilterDialog.kt new file mode 100644 index 000000000..685f49cff --- /dev/null +++ b/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountsTransactionFilterDialog.kt @@ -0,0 +1,280 @@ +package org.mifos.mobile.ui.savings_account_transaction + + +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.gestures.scrollable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Card +import androidx.compose.material3.CheckboxDefaults +import androidx.compose.material3.DatePicker +import androidx.compose.material3.DatePickerDialog +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.rememberDatePickerState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +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.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import org.mifos.mobile.R +import org.mifos.mobile.core.ui.component.MifosCheckBox +import org.mifos.mobile.core.ui.component.MifosIconTextButton +import org.mifos.mobile.core.ui.component.MifosIcons +import org.mifos.mobile.core.ui.component.MifosRadioButton +import org.mifos.mobile.core.ui.theme.MifosMobileTheme +import org.mifos.mobile.core.common.utils.DateHelper +import org.mifos.mobile.core.common.utils.DateHelper.getDateAsStringFromLong +import java.time.Instant + +@Composable +fun SavingsTransactionFilterDialog( + onDismiss: () -> Unit, + savingsTransactionFilterDataModel: SavingsTransactionFilterDataModel, + filter: (SavingsTransactionFilterDataModel) -> Unit, +) { + + var radioFilter by rememberSaveable { mutableStateOf(savingsTransactionFilterDataModel.radioFilter) } + val checkBoxFilters by rememberSaveable { mutableStateOf(savingsTransactionFilterDataModel.checkBoxFilters) } + var startDate by rememberSaveable { mutableStateOf(savingsTransactionFilterDataModel.startDate) } + var endDate by rememberSaveable { mutableStateOf(savingsTransactionFilterDataModel.endDate) } + + var isDepositChecked by rememberSaveable { mutableStateOf(false) } + var isDividendPayoutChecked by rememberSaveable { mutableStateOf(false) } + var isWithdrawalChecked by rememberSaveable { mutableStateOf(false) } + var isInterestPostingChecked by rememberSaveable { mutableStateOf(false) } + + LaunchedEffect(key1 = checkBoxFilters) { + checkBoxFilters.forEach { filter -> + when(filter) { + SavingsTransactionCheckBoxFilter.DEPOSIT -> isDepositChecked = true + SavingsTransactionCheckBoxFilter.DIVIDEND_PAYOUT -> isDividendPayoutChecked = true + SavingsTransactionCheckBoxFilter.WITHDRAWAL -> isWithdrawalChecked = true + SavingsTransactionCheckBoxFilter.INTEREST_POSTING -> isInterestPostingChecked = true + } + } + } + + Dialog( + onDismissRequest = { onDismiss.invoke() }, + ) { + Card(shape = RoundedCornerShape(20.dp)) { + Column( + modifier = Modifier.padding(vertical = 20.dp, horizontal = 10.dp) + ) { + Text(text = stringResource(id = R.string.select_you_want)) + + Spacer(modifier = Modifier.height(20.dp)) + + SavingsTransactionFilterDialogContent( + selectedStartDate = startDate, + selectedEndDate = endDate, + radioFilter = radioFilter, + selectRadioFilter = { radioFilter = it }, + setStartDate = { startDate = it }, + isDepositChecked = isDepositChecked, + isWithdrawalChecked = isWithdrawalChecked, + isInterestPostingChecked = isInterestPostingChecked, + isDividendPayoutChecked = isDividendPayoutChecked, + setEndDate = { endDate = it }, + toggleCheckBox = { filter, isEnabled -> + when(filter) { + SavingsTransactionCheckBoxFilter.DEPOSIT -> isDepositChecked = isEnabled + SavingsTransactionCheckBoxFilter.DIVIDEND_PAYOUT -> isDividendPayoutChecked = isEnabled + SavingsTransactionCheckBoxFilter.WITHDRAWAL -> isWithdrawalChecked = isEnabled + SavingsTransactionCheckBoxFilter.INTEREST_POSTING -> isInterestPostingChecked = isEnabled + } + if(isEnabled) checkBoxFilters.add(filter) + else checkBoxFilters.remove(filter) + } + ) + + Spacer(modifier = Modifier.height(20.dp)) + + Row { + TextButton( + onClick = { + radioFilter = null + isDepositChecked = false + isWithdrawalChecked = false + isInterestPostingChecked = false + isDividendPayoutChecked = false + checkBoxFilters.clear() + } + ) { + Text(text = stringResource(id = R.string.clear_filters)) + } + + Spacer(modifier = Modifier.weight(1f)) + + TextButton( + onClick = { onDismiss() } + ) { + Text(text = stringResource(id = R.string.cancel)) + } + + TextButton( + onClick = { + onDismiss() + filter( + SavingsTransactionFilterDataModel( + startDate = startDate, + endDate = endDate, + radioFilter = radioFilter, + checkBoxFilters = checkBoxFilters + ) + ) + } + ) { + Text(text = stringResource(id = R.string.filter)) + } + } + } + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SavingsTransactionFilterDialogContent( + selectedStartDate: Long, + selectedEndDate: Long, + radioFilter: SavingsTransactionRadioFilter?, + selectRadioFilter: (SavingsTransactionRadioFilter) -> Unit, + isDepositChecked: Boolean, + isDividendPayoutChecked: Boolean, + isWithdrawalChecked: Boolean, + isInterestPostingChecked: Boolean, + toggleCheckBox: (SavingsTransactionCheckBoxFilter, Boolean) -> Unit, + setStartDate: (Long) -> Unit, + setEndDate: (Long) -> Unit +) { + val scrollState = rememberScrollState() + var showStartDatePickerDialog by rememberSaveable { mutableStateOf(false) } + var showEndDatePickerDialog by rememberSaveable { mutableStateOf(false) } + val startDatePickerState = rememberDatePickerState(initialSelectedDateMillis = selectedStartDate) + val endDatePickerState = rememberDatePickerState(initialSelectedDateMillis = selectedEndDate) + var isDatesEnabled by rememberSaveable { mutableStateOf(false) } + + LaunchedEffect(key1 = radioFilter) { + isDatesEnabled = radioFilter == SavingsTransactionRadioFilter.DATE + + when (radioFilter) { + SavingsTransactionRadioFilter.FOUR_WEEKS -> { + setStartDate(DateHelper.subtractWeeks(4)) + setEndDate(System.currentTimeMillis()) + } + + SavingsTransactionRadioFilter.THREE_MONTHS -> { + setStartDate(DateHelper.subtractMonths(3)) + setEndDate(System.currentTimeMillis()) + } + + SavingsTransactionRadioFilter.SIX_MONTHS -> { + setStartDate(DateHelper.subtractMonths(6)) + setEndDate(System.currentTimeMillis()) + } + + else -> Unit + } + } + + Column( + modifier = Modifier.scrollable(state = scrollState, orientation = Orientation.Vertical) + ) { + SavingsTransactionRadioFilter.entries.forEach { filter -> + MifosRadioButton( + selected = radioFilter == filter, + onClick = { selectRadioFilter(filter) }, + textResId = filter.textResId + ) + + if (filter == SavingsTransactionRadioFilter.DATE) { + Row { + MifosIconTextButton( + text = getDateAsStringFromLong(selectedStartDate), + imageVector = MifosIcons.Edit, + enabled = radioFilter == SavingsTransactionRadioFilter.DATE, + onClick = { showStartDatePickerDialog = true } + ) + MifosIconTextButton( + text = getDateAsStringFromLong(selectedEndDate), + imageVector = MifosIcons.Edit, + enabled = radioFilter == SavingsTransactionRadioFilter.DATE, + onClick = { showEndDatePickerDialog = true } + ) + } + } + } + + SavingsTransactionCheckBoxFilter.entries.forEach { filter -> + MifosCheckBox( + checked = when(filter) { + SavingsTransactionCheckBoxFilter.DEPOSIT -> isDepositChecked + SavingsTransactionCheckBoxFilter.DIVIDEND_PAYOUT -> isDividendPayoutChecked + SavingsTransactionCheckBoxFilter.WITHDRAWAL -> isWithdrawalChecked + SavingsTransactionCheckBoxFilter.INTEREST_POSTING -> isInterestPostingChecked + }, + onCheckChanged = { + toggleCheckBox(filter, it) + }, + text = stringResource(id = filter.textResId), + checkboxColors = CheckboxDefaults.colors().copy( + checkedBorderColor = filter.checkBoxColor, + uncheckedBorderColor = filter.checkBoxColor, + checkedBoxColor = filter.checkBoxColor, + ) + ) + } + } + + if (showStartDatePickerDialog) { + DatePickerDialog( + onDismissRequest = { showStartDatePickerDialog = false }, + confirmButton = { + startDatePickerState.selectedDateMillis?.let{ setStartDate(it) } + } + ) { DatePicker(state = startDatePickerState) } + } + + if (showEndDatePickerDialog) { + DatePickerDialog( + onDismissRequest = { showEndDatePickerDialog = false }, + confirmButton = { + endDatePickerState.selectedDateMillis?.let { setEndDate(it) } + } + ) { DatePicker(state = endDatePickerState) } + } +} + + +@Preview +@Composable +fun SavingsTransactionFilterDialogPreview() { + MifosMobileTheme { + SavingsTransactionFilterDialog( + savingsTransactionFilterDataModel = SavingsTransactionFilterDataModel( + radioFilter = null, + checkBoxFilters = mutableListOf(), + startDate = Instant.now().toEpochMilli(), + endDate = Instant.now().toEpochMilli() + ), + filter = {}, + onDismiss = {}, + ) + } +} + + diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountsTransactionScreen.kt b/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountsTransactionScreen.kt new file mode 100644 index 000000000..a0b137f13 --- /dev/null +++ b/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountsTransactionScreen.kt @@ -0,0 +1,161 @@ +package org.mifos.mobile.ui.savings_account_transaction + + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +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.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import org.mifos.mobile.R +import org.mifos.mobile.core.ui.component.EmptyDataView +import org.mifos.mobile.core.ui.component.MFScaffold +import org.mifos.mobile.core.ui.component.MifosErrorComponent +import org.mifos.mobile.core.ui.component.MifosIcons +import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay +import org.mifos.mobile.core.ui.component.MifosTopBar +import org.mifos.mobile.core.ui.theme.MifosMobileTheme +import org.mifos.mobile.core.common.Network +import org.mifos.mobile.core.model.entity.accounts.savings.Transactions +import java.time.Instant + +@Composable +fun SavingsAccountTransactionScreen( + viewModel: SavingAccountsTransactionViewModel = hiltViewModel(), + navigateBack: () -> Unit, +) { + val uiState by viewModel.savingAccountsTransactionUiState.collectAsStateWithLifecycle() + SavingsAccountTransactionScreen( + uiState = uiState, + navigateBack = navigateBack, + retryConnection = { viewModel.loadSavingsWithAssociations(viewModel.savingsId) }, + filterList = { viewModel.filterList(filter = it) } + ) +} + +@Composable +fun SavingsAccountTransactionScreen( + uiState: SavingsAccountTransactionUiState, + navigateBack: () -> Unit, + retryConnection: () -> Unit, + filterList: (SavingsTransactionFilterDataModel) -> Unit, +) { + val context = LocalContext.current + var transactionList by rememberSaveable { mutableStateOf(listOf()) } + var isDialogOpen by rememberSaveable { mutableStateOf(false) } + var savingsTransactionFilterDataModel by rememberSaveable { + mutableStateOf( + SavingsTransactionFilterDataModel( + startDate = Instant.now().toEpochMilli(), + endDate = Instant.now().toEpochMilli(), + radioFilter = null, + checkBoxFilters = mutableListOf() + ) + ) + } + + MFScaffold( + topBar = { + MifosTopBar( + navigateBack = navigateBack, + title = { + Text( + text = stringResource(id = R.string.savings_account_transaction), + overflow = TextOverflow.Ellipsis, + maxLines = 1 + ) + }, + actions = { + IconButton(onClick = { isDialogOpen = true }) { + Icon( + imageVector = MifosIcons.FilterList, + contentDescription = null, + tint = MaterialTheme.colorScheme.onSurface + ) + } + } + ) + }, + scaffoldContent = { paddingValues -> + Box(modifier = Modifier.padding(paddingValues = paddingValues)) { + when (uiState) { + is SavingsAccountTransactionUiState.Loading -> { + MifosProgressIndicatorOverlay() + } + + is SavingsAccountTransactionUiState.Error -> { + MifosErrorComponent( + isNetworkConnected = Network.isConnected(context), + isEmptyData = false, + isRetryEnabled = true, + onRetry = retryConnection + ) + } + + is SavingsAccountTransactionUiState.Success -> { + if (uiState.savingAccountsTransactionList.isNullOrEmpty()) { + EmptyDataView( + icon = R.drawable.ic_compare_arrows_black_24dp, + error = R.string.no_transaction_found + ) + } else { + transactionList = uiState.savingAccountsTransactionList + SavingsAccountTransactionContent(transactionList = transactionList) + } + } + } + } + } + ) + + if (isDialogOpen) { + SavingsTransactionFilterDialog( + savingsTransactionFilterDataModel = savingsTransactionFilterDataModel, + onDismiss = { isDialogOpen = false }, + filter = { filters -> + savingsTransactionFilterDataModel = filters + filterList(filters) + }, + ) + } +} + +class SavingsAccountTransactionUiStatesParameterProvider : + PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + SavingsAccountTransactionUiState.Success(listOf()), + SavingsAccountTransactionUiState.Error(""), + SavingsAccountTransactionUiState.Loading, + ) +} + +@Preview(showSystemUi = true) +@Composable +fun SavingsAccountTransactionScreenPreview( + @PreviewParameter(SavingsAccountTransactionUiStatesParameterProvider::class) savingsAccountUiState: SavingsAccountTransactionUiState +) { + MifosMobileTheme { + SavingsAccountTransactionScreen( + uiState = savingsAccountUiState, + navigateBack = { }, + retryConnection = { }, + filterList = { } + ) + } +} + diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountsTransactionViewModel.kt b/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountsTransactionViewModel.kt new file mode 100644 index 000000000..fdfb02fec --- /dev/null +++ b/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountsTransactionViewModel.kt @@ -0,0 +1,203 @@ +package org.mifos.mobile.ui.savings_account_transaction + + +import android.os.Parcelable +import androidx.compose.ui.graphics.Color +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.launch +import kotlinx.parcelize.Parcelize +import org.mifos.mobile.R +import org.mifos.mobile.core.ui.theme.DepositGreen +import org.mifos.mobile.core.ui.theme.GreenSuccess +import org.mifos.mobile.core.ui.theme.RedLight +import org.mifos.mobile.core.data.repositories.SavingsAccountRepository +import org.mifos.mobile.core.model.entity.accounts.savings.TransactionType +import org.mifos.mobile.core.model.entity.accounts.savings.Transactions +import org.mifos.mobile.core.common.utils.DateHelper +import javax.inject.Inject + +@HiltViewModel +class SavingAccountsTransactionViewModel @Inject constructor(private val savingsAccountRepositoryImp: SavingsAccountRepository) : + ViewModel() { + + private val _savingAccountsTransactionUiState = MutableStateFlow(SavingsAccountTransactionUiState.Loading) + val savingAccountsTransactionUiState: StateFlow get() = _savingAccountsTransactionUiState + + private var _transactionsList: List = mutableListOf() + private val transactionsList: List get() = _transactionsList + + private var _savingsId: Long = 0 + val savingsId get() = _savingsId + + fun setSavingsId(savingsId: Long) { + _savingsId = savingsId + loadSavingsWithAssociations(savingsId) + } + + fun loadSavingsWithAssociations(accountId: Long) { + viewModelScope.launch { + _savingAccountsTransactionUiState.value = SavingsAccountTransactionUiState.Loading + savingsAccountRepositoryImp.getSavingsWithAssociations( + accountId, + org.mifos.mobile.core.common.Constants.TRANSACTIONS, + ).catch { + _savingAccountsTransactionUiState.value = + SavingsAccountTransactionUiState.Error(it.message) + }.collect { + _transactionsList = it.transactions + _savingAccountsTransactionUiState.value = + SavingsAccountTransactionUiState.Success(it.transactions) + } + } + } + + fun filterList(filter: SavingsTransactionFilterDataModel) { + when { + filter.radioFilter != null && filter.checkBoxFilters.isNotEmpty() -> { + filterByDateAndType( + startDate = filter.startDate, + endDate = filter.endDate, + checkBoxFilters = filter.checkBoxFilters + ) + } + filter.radioFilter != null -> { + filterByDate( + startDate = filter.startDate, + endDate = filter.endDate + ) + } + filter.checkBoxFilters.isNotEmpty() -> { + filterByType( + checkBoxFilters = filter.checkBoxFilters + ) + } + else -> { + _savingAccountsTransactionUiState.value = SavingsAccountTransactionUiState.Success(transactionsList) + } + } + } + + + private fun filterByDateAndType( + startDate: Long, + endDate: Long, + checkBoxFilters: MutableList + ) { + val typeFilteredList = filterSavingsAccountTransactionsByType(checkBoxFilters = checkBoxFilters) + val dateAndTypeFilteredList = filterTransactionListByDate(transactions = typeFilteredList, startDate = startDate, endDate = endDate) + _savingAccountsTransactionUiState.value = SavingsAccountTransactionUiState.Success(dateAndTypeFilteredList) + } + + private fun filterByDate(startDate: Long, endDate: Long) { + val list = filterTransactionListByDate(transactions = transactionsList, startDate = startDate, endDate = endDate) + _savingAccountsTransactionUiState.value = SavingsAccountTransactionUiState.Success(list) + } + + private fun filterByType( + checkBoxFilters: MutableList + ) { + val list = filterSavingsAccountTransactionsByType(checkBoxFilters = checkBoxFilters) + _savingAccountsTransactionUiState.value = SavingsAccountTransactionUiState.Success(list) + } + + private fun filterTransactionListByDate( + transactions: List, + startDate: Long, + endDate: Long + ): List { + return transactions.filter { + (DateHelper.getDateAsLongFromList(it.date) in startDate..endDate) + } + } + + private fun filterSavingsAccountTransactionsByType( + checkBoxFilters: MutableList + ): List { + var filteredSavingsTransactions: List = ArrayList() + checkBoxFilters.forEach { filter -> + val list = when (filter) { + SavingsTransactionCheckBoxFilter.DEPOSIT -> { + transactionsList.filter { it.transactionType?.deposit == true } + } + SavingsTransactionCheckBoxFilter.DIVIDEND_PAYOUT -> { + transactionsList.filter { it.transactionType?.dividendPayout == true } + } + SavingsTransactionCheckBoxFilter.WITHDRAWAL -> { + transactionsList.filter { it.transactionType?.withdrawal == true } + } + SavingsTransactionCheckBoxFilter.INTEREST_POSTING -> { + transactionsList.filter { it.transactionType?.interestPosting == true } + } + } + filteredSavingsTransactions = filteredSavingsTransactions.plus(list) + } + return filteredSavingsTransactions + } +} + +sealed class SavingsAccountTransactionUiState { + data object Loading : SavingsAccountTransactionUiState() + data class Error(val errorMessage: String?) : SavingsAccountTransactionUiState() + data class Success(val savingAccountsTransactionList: List?) : SavingsAccountTransactionUiState() +} + +fun getTransactionTriangleResId(transactionType: TransactionType?): Int { + return transactionType?.run { + when { + deposit == true -> R.drawable.triangular_green_view + dividendPayout == true -> R.drawable.triangular_red_view + withdrawal == true -> R.drawable.triangular_red_view + interestPosting == true -> R.drawable.triangular_green_view + feeDeduction == true -> R.drawable.triangular_red_view + initiateTransfer == true -> R.drawable.triangular_red_view + approveTransfer == true -> R.drawable.triangular_red_view + withdrawTransfer == true -> R.drawable.triangular_red_view + rejectTransfer == true -> R.drawable.triangular_green_view + overdraftFee == true -> R.drawable.triangular_red_view + else -> R.drawable.triangular_green_view + } + } ?: R.drawable.triangular_red_view +} + +@Parcelize +data class SavingsTransactionFilterDataModel( + val startDate: Long, + val endDate: Long, + val radioFilter: SavingsTransactionRadioFilter?, + val checkBoxFilters: MutableList +): Parcelable + +enum class SavingsTransactionRadioFilter(val textResId: Int) { + DATE(textResId = R.string.date), + FOUR_WEEKS(textResId = R.string.four_weeks), + THREE_MONTHS(textResId = R.string.three_months), + SIX_MONTHS(textResId = R.string.six_months) +} + +enum class SavingsTransactionCheckBoxFilter( + val textResId: Int, + val checkBoxColor: Color, +) { + DEPOSIT( + textResId = R.string.deposit, + checkBoxColor = DepositGreen + ), + DIVIDEND_PAYOUT( + textResId = R.string.dividend_payout, + checkBoxColor = RedLight + ), + WITHDRAWAL( + textResId = R.string.withdrawal, + checkBoxColor = RedLight + ), + INTEREST_POSTING( + textResId = R.string.interest_posting, + checkBoxColor = GreenSuccess + ) +} + diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingsAccountTransactionComposeFragment.kt b/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingsAccountTransactionComposeFragment.kt new file mode 100644 index 000000000..9c57916be --- /dev/null +++ b/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingsAccountTransactionComposeFragment.kt @@ -0,0 +1,51 @@ +package org.mifos.mobile.ui.savings_account_transaction + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.viewModels +import dagger.hilt.android.AndroidEntryPoint +import org.mifos.mobile.core.ui.component.mifosComposeView +import org.mifos.mobile.ui.activities.SavingsAccountContainerActivity +import org.mifos.mobile.ui.fragments.base.BaseFragment +import org.mifos.mobile.core.common.Constants + + +@AndroidEntryPoint +class SavingAccountsTransactionComposeFragment : BaseFragment() { + + private val viewModel: SavingAccountsTransactionViewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + (activity as? SavingsAccountContainerActivity)?.hideToolbar() + if (arguments != null) { + arguments?.getLong(Constants.SAVINGS_ID)?.let { + viewModel.setSavingsId(it) + } + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + return mifosComposeView(requireContext()) { + SavingsAccountTransactionScreen( + navigateBack = { activity?.supportFragmentManager?.popBackStack() }, + ) + } + } + + companion object { + fun newInstance(savingsId: Long?): SavingAccountsTransactionComposeFragment { + val fragment = SavingAccountsTransactionComposeFragment() + val args = Bundle() + if (savingsId != null) args.putLong(Constants.SAVINGS_ID, savingsId) + fragment.arguments = args + return fragment + } + } +} diff --git a/feature/registration/src/main/java/org/mifos/mobile/feature/registration/screens/RegistrationVerificationScreen.kt b/feature/registration/src/main/java/org/mifos/mobile/feature/registration/screens/RegistrationVerificationScreen.kt index 179373f17..a5e162e7d 100644 --- a/feature/registration/src/main/java/org/mifos/mobile/feature/registration/screens/RegistrationVerificationScreen.kt +++ b/feature/registration/src/main/java/org/mifos/mobile/feature/registration/screens/RegistrationVerificationScreen.kt @@ -18,7 +18,6 @@ import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable