Added a filter bar to filter transactions by date

This commit is contained in:
dankito 2024-09-06 15:40:49 +02:00
parent 77c944d33b
commit 43bd89a047
8 changed files with 113 additions and 7 deletions

View File

@ -51,6 +51,8 @@ fun App() {
val snackbarHostState = remember { SnackbarHostState() } val snackbarHostState = remember { SnackbarHostState() }
val showFilterBar = uiState.showFilterBar.collectAsState()
var isInitialized by remember { mutableStateOf(false) } var isInitialized by remember { mutableStateOf(false) }
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
@ -98,6 +100,10 @@ fun App() {
DesktopLayout(scaffoldPadding, uiState, uiSettings, snackbarHostState, desktopDrawerWidth) DesktopLayout(scaffoldPadding, uiState, uiSettings, snackbarHostState, desktopDrawerWidth)
} }
} }
if (showFilterBar.value) {
FilterBar()
}
} }

View File

@ -127,6 +127,7 @@ fun BottomBar(showMenuDrawer: Boolean = true) {
} }
} }
if (userAccounts.isNotEmpty()) { if (userAccounts.isNotEmpty()) {
if (showSearchbar == false) { if (showSearchbar == false) {
Row(Modifier.fillMaxHeight().widthIn(IconWidth, IconWidth), verticalAlignment = Alignment.CenterVertically) { Row(Modifier.fillMaxHeight().widthIn(IconWidth, IconWidth), verticalAlignment = Alignment.CenterVertically) {
@ -137,6 +138,10 @@ fun BottomBar(showMenuDrawer: Boolean = true) {
} }
Row(Modifier.fillMaxHeight().widthIn(IconWidth.times(2), IconWidth.times(2)), verticalAlignment = Alignment.CenterVertically) { Row(Modifier.fillMaxHeight().widthIn(IconWidth.times(2), IconWidth.times(2)), verticalAlignment = Alignment.CenterVertically) {
IconButton({ uiState.showFilterBar.value = !!!uiState.showFilterBar.value }, Modifier.width(IconWidth)) {
Icon(imageResource(Res.drawable.filter_alt), "Kontoumsätze nach Konto, Zeitraum oder Betrag filtern", Modifier.size(24.dp))
}
IconButton({ coroutineScope.launch { DI.bankingService.updateAccountTransactions() } }, Modifier.width(IconWidth)) { // TODO: use sync, cached or autorenew as icon? IconButton({ coroutineScope.launch { DI.bankingService.updateAccountTransactions() } }, Modifier.width(IconWidth)) { // TODO: use sync, cached or autorenew as icon?
Icon(Icons.Filled.Refresh, contentDescription = "Neue Kontoumsätze abholen") Icon(Icons.Filled.Refresh, contentDescription = "Neue Kontoumsätze abholen")
} }

View File

@ -0,0 +1,73 @@
package net.codinux.banking.ui.appskeleton
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import net.codinux.banking.ui.config.DI
import net.codinux.banking.ui.forms.RoundedCornersCard
import net.codinux.banking.ui.forms.Select
private val uiState = DI.uiState
private val uiSettings = DI.uiSettings
val labelsWidth = 60.dp
val selectBoxesWidth = 154.dp
val horizontalPadding = 6.dp
@Composable
fun FilterBar() {
val transactions = uiState.transactions.collectAsState()
val transactionsFilter = uiState.transactionsFilter.collectAsState()
val filterService = DI.accountTransactionsFilterService
val years by remember(transactions) { derivedStateOf { filterService.getYearForWhichWeHaveTransactions(transactions.value).sorted() + listOf(null as? Int) } }
val months = listOf("Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember" /*, "1. Quartal", "2. Quartal", "3. Quartal", "4. Quartal" */, null)
Box(
contentAlignment = Alignment.BottomEnd,
modifier = Modifier.fillMaxSize().zIndex(1100f)
.padding(bottom = 64.dp, end = 74.dp)
) {
Column(Modifier.height(166.dp).width(390.dp)) {
RoundedCornersCard(cornerSize = 4.dp, shadowElevation = 24.dp) {
Column(Modifier.fillMaxWidth().background(Color.White).padding(16.dp)) {
Row(Modifier.fillMaxWidth().padding(top = 8.dp), verticalAlignment = Alignment.CenterVertically) {
Text("Zeitraum", Modifier.width(labelsWidth))
Select(
label = "Jahr",
items = years,
selectedItem = transactionsFilter.value.year,
onSelectedItemChanged = { year -> transactionsFilter.value.year = year },
getItemDisplayText = { year -> if (year == null) "" else year.toString() },
modifier = Modifier.width(selectBoxesWidth).padding(horizontal = horizontalPadding)
)
if (transactionsFilter.value.year != null) {
Select(
label ="Monat",
items = months,
selectedItem = transactionsFilter.value.month?.let { months[it - 1] },
onSelectedItemChanged = { month -> transactionsFilter.value.month = months.indexOf(month) + 1 },
getItemDisplayText = { month -> if (month == null) "" else month },
modifier = Modifier.width(selectBoxesWidth)
)
}
}
}
}
}
}
}

View File

@ -120,7 +120,7 @@ fun TransferMoneyDialog(
"Konto", "Konto",
accountsSupportingTransferringMoney, senderAccount, { senderAccount = it }, accountsSupportingTransferringMoney, senderAccount, { senderAccount = it },
{ account -> "${accountsToUserAccount[account]?.displayName} ${account.displayName}" }, { account -> "${accountsToUserAccount[account]?.displayName} ${account.displayName}" },
{ BankIcon(accountsToUserAccount[senderAccount]) } leadingIcon = { BankIcon(accountsToUserAccount[senderAccount]) }
) { account -> ) { account ->
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
BankIcon(accountsToUserAccount[account], Modifier.padding(end = 6.dp)) BankIcon(accountsToUserAccount[account], Modifier.padding(end = 6.dp))

View File

@ -13,12 +13,13 @@ fun <T> Select(
selectedItem: T, selectedItem: T,
onSelectedItemChanged: (T) -> Unit, onSelectedItemChanged: (T) -> Unit,
getItemDisplayText: (T) -> String, getItemDisplayText: (T) -> String,
modifier: Modifier = Modifier,
leadingIcon: @Composable (() -> Unit)? = null, leadingIcon: @Composable (() -> Unit)? = null,
dropDownItemContent: @Composable ((T) -> Unit)? = null dropDownItemContent: @Composable ((T) -> Unit)? = null
) { ) {
var showDropDownMenu by remember { mutableStateOf(false) } var showDropDownMenu by remember { mutableStateOf(false) }
ExposedDropdownMenuBox(showDropDownMenu, { isExpanded -> showDropDownMenu = isExpanded }, Modifier.fillMaxWidth()) { ExposedDropdownMenuBox(showDropDownMenu, { isExpanded -> showDropDownMenu = isExpanded }, modifier.fillMaxWidth()) {
OutlinedTextField( OutlinedTextField(
value = getItemDisplayText(selectedItem), value = getItemDisplayText(selectedItem),
onValueChange = { }, onValueChange = { },

View File

@ -7,7 +7,7 @@ import net.codinux.banking.dataaccess.entities.UserAccountEntity
class AccountTransactionsFilter { class AccountTransactionsFilter {
val noFiltersApplied: Boolean val noFiltersApplied: Boolean
get() = showAllAccounts && noSearchTermApplied get() = showAllAccounts && noSearchTermApplied && noDateFilterApplied
var selectedAccounts = mutableStateOf<List<BankAccountFilter>>(emptyList()) var selectedAccounts = mutableStateOf<List<BankAccountFilter>>(emptyList())
@ -37,4 +37,13 @@ class AccountTransactionsFilter {
fun updateSearchTerm(searchTerm: String) { fun updateSearchTerm(searchTerm: String) {
this.searchTerm = searchTerm this.searchTerm = searchTerm
} }
var year by mutableStateOf<Int?>(null)
var month by mutableStateOf<Int?>(null)
val noDateFilterApplied: Boolean
get() = year == null && month == null
} }

View File

@ -8,14 +8,20 @@ import net.codinux.banking.ui.model.BankAccountFilter
class AccountTransactionsFilterService { class AccountTransactionsFilterService {
fun filterAccounts(transactions: List<AccountTransactionViewModel>, transactionsFilter: AccountTransactionsFilter): List<AccountTransactionViewModel> { fun filterAccounts(transactions: List<AccountTransactionViewModel>, filter: AccountTransactionsFilter): List<AccountTransactionViewModel> {
val appliedAccountFilter = if (transactionsFilter.showAllAccounts) { var appliedAccountFilter = if (filter.showAllAccounts) {
transactions transactions
} else { } else {
transactions.filter { matchesFilter(it, transactionsFilter.selectedAccounts.value) } transactions.filter { matchesFilter(it, filter.selectedAccounts.value) }
} }
val searchTerm = transactionsFilter.searchTerm filter.year?.let { year ->
val month = filter.month
appliedAccountFilter = appliedAccountFilter.filter { it.valueDate.year == year && (month == null || it.valueDate.monthNumber == month) }
}
val searchTerm = filter.searchTerm
return if (searchTerm.isBlank()) { return if (searchTerm.isBlank()) {
appliedAccountFilter appliedAccountFilter
} else { } else {
@ -57,4 +63,8 @@ class AccountTransactionsFilterService {
return filter?.bankAccount == bankAccount return filter?.bankAccount == bankAccount
} }
fun getYearForWhichWeHaveTransactions(transactions: List<AccountTransactionViewModel>): List<Int> =
transactions.map { it.valueDate.year }.toSet().sortedDescending()
} }

View File

@ -38,6 +38,8 @@ class UiState : ViewModel() {
val transactionsFilter = MutableStateFlow(AccountTransactionsFilter()) val transactionsFilter = MutableStateFlow(AccountTransactionsFilter())
var showFilterBar = MutableStateFlow(false)
val showAddAccountDialog = MutableStateFlow(false) val showAddAccountDialog = MutableStateFlow(false)