From 43bd89a047b9e77757970da8acd072b2db8c3952 Mon Sep 17 00:00:00 2001 From: dankito Date: Fri, 6 Sep 2024 15:40:49 +0200 Subject: [PATCH] Added a filter bar to filter transactions by date --- .../kotlin/net/codinux/banking/ui/App.kt | 6 ++ .../banking/ui/appskeleton/BottomBar.kt | 5 ++ .../banking/ui/appskeleton/FilterBar.kt | 73 +++++++++++++++++++ .../banking/ui/dialogs/TransferMoneyDialog.kt | 2 +- .../net/codinux/banking/ui/forms/Select.kt | 3 +- .../ui/model/AccountTransactionsFilter.kt | 11 ++- .../AccountTransactionsFilterService.kt | 18 ++++- .../net/codinux/banking/ui/state/UiState.kt | 2 + 8 files changed, 113 insertions(+), 7 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/net/codinux/banking/ui/appskeleton/FilterBar.kt diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/App.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/App.kt index 62bbd96..5babba6 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/App.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/App.kt @@ -51,6 +51,8 @@ fun App() { val snackbarHostState = remember { SnackbarHostState() } + val showFilterBar = uiState.showFilterBar.collectAsState() + var isInitialized by remember { mutableStateOf(false) } val coroutineScope = rememberCoroutineScope() @@ -98,6 +100,10 @@ fun App() { DesktopLayout(scaffoldPadding, uiState, uiSettings, snackbarHostState, desktopDrawerWidth) } } + + if (showFilterBar.value) { + FilterBar() + } } diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/appskeleton/BottomBar.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/appskeleton/BottomBar.kt index 0c209f6..84dc5b7 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/appskeleton/BottomBar.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/appskeleton/BottomBar.kt @@ -127,6 +127,7 @@ fun BottomBar(showMenuDrawer: Boolean = true) { } } + if (userAccounts.isNotEmpty()) { if (showSearchbar == false) { 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) { + 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? Icon(Icons.Filled.Refresh, contentDescription = "Neue Kontoumsätze abholen") } diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/appskeleton/FilterBar.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/appskeleton/FilterBar.kt new file mode 100644 index 0000000..0b6d381 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/appskeleton/FilterBar.kt @@ -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) + ) + } + } + } + } + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/TransferMoneyDialog.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/TransferMoneyDialog.kt index c18118f..cb4b516 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/TransferMoneyDialog.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/TransferMoneyDialog.kt @@ -120,7 +120,7 @@ fun TransferMoneyDialog( "Konto", accountsSupportingTransferringMoney, senderAccount, { senderAccount = it }, { account -> "${accountsToUserAccount[account]?.displayName} ${account.displayName}" }, - { BankIcon(accountsToUserAccount[senderAccount]) } + leadingIcon = { BankIcon(accountsToUserAccount[senderAccount]) } ) { account -> Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { BankIcon(accountsToUserAccount[account], Modifier.padding(end = 6.dp)) diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/forms/Select.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/forms/Select.kt index 29ca7fe..46d35ab 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/forms/Select.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/forms/Select.kt @@ -13,12 +13,13 @@ fun Select( selectedItem: T, onSelectedItemChanged: (T) -> Unit, getItemDisplayText: (T) -> String, + modifier: Modifier = Modifier, leadingIcon: @Composable (() -> Unit)? = null, dropDownItemContent: @Composable ((T) -> Unit)? = null ) { var showDropDownMenu by remember { mutableStateOf(false) } - ExposedDropdownMenuBox(showDropDownMenu, { isExpanded -> showDropDownMenu = isExpanded }, Modifier.fillMaxWidth()) { + ExposedDropdownMenuBox(showDropDownMenu, { isExpanded -> showDropDownMenu = isExpanded }, modifier.fillMaxWidth()) { OutlinedTextField( value = getItemDisplayText(selectedItem), onValueChange = { }, diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/AccountTransactionsFilter.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/AccountTransactionsFilter.kt index 0e3e7f6..da9cbc7 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/AccountTransactionsFilter.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/AccountTransactionsFilter.kt @@ -7,7 +7,7 @@ import net.codinux.banking.dataaccess.entities.UserAccountEntity class AccountTransactionsFilter { val noFiltersApplied: Boolean - get() = showAllAccounts && noSearchTermApplied + get() = showAllAccounts && noSearchTermApplied && noDateFilterApplied var selectedAccounts = mutableStateOf>(emptyList()) @@ -37,4 +37,13 @@ class AccountTransactionsFilter { fun updateSearchTerm(searchTerm: String) { this.searchTerm = searchTerm } + + + var year by mutableStateOf(null) + + var month by mutableStateOf(null) + + val noDateFilterApplied: Boolean + get() = year == null && month == null + } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/service/AccountTransactionsFilterService.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/service/AccountTransactionsFilterService.kt index 5ec14df..08c614a 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/service/AccountTransactionsFilterService.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/service/AccountTransactionsFilterService.kt @@ -8,14 +8,20 @@ import net.codinux.banking.ui.model.BankAccountFilter class AccountTransactionsFilterService { - fun filterAccounts(transactions: List, transactionsFilter: AccountTransactionsFilter): List { - val appliedAccountFilter = if (transactionsFilter.showAllAccounts) { + fun filterAccounts(transactions: List, filter: AccountTransactionsFilter): List { + var appliedAccountFilter = if (filter.showAllAccounts) { transactions } 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()) { appliedAccountFilter } else { @@ -57,4 +63,8 @@ class AccountTransactionsFilterService { return filter?.bankAccount == bankAccount } + + fun getYearForWhichWeHaveTransactions(transactions: List): List = + transactions.map { it.valueDate.year }.toSet().sortedDescending() + } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/state/UiState.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/state/UiState.kt index c9a170e..bfef8b5 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/state/UiState.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/state/UiState.kt @@ -38,6 +38,8 @@ class UiState : ViewModel() { val transactionsFilter = MutableStateFlow(AccountTransactionsFilter()) + var showFilterBar = MutableStateFlow(false) + val showAddAccountDialog = MutableStateFlow(false)