Implemented different kinds of grouping the transactions
This commit is contained in:
parent
43bd89a047
commit
d792384efc
|
@ -10,6 +10,7 @@ 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.config.Internationalization
|
||||
import net.codinux.banking.ui.forms.RoundedCornersCard
|
||||
import net.codinux.banking.ui.forms.Select
|
||||
|
||||
|
@ -17,11 +18,11 @@ private val uiState = DI.uiState
|
|||
|
||||
private val uiSettings = DI.uiSettings
|
||||
|
||||
val labelsWidth = 60.dp
|
||||
private val labelsWidth = 60.dp
|
||||
|
||||
val selectBoxesWidth = 154.dp
|
||||
private val selectBoxesWidth = 154.dp
|
||||
|
||||
val horizontalPadding = 6.dp
|
||||
private val horizontalPadding = 6.dp
|
||||
|
||||
@Composable
|
||||
fun FilterBar() {
|
||||
|
@ -29,6 +30,8 @@ fun FilterBar() {
|
|||
|
||||
val transactionsFilter = uiState.transactionsFilter.collectAsState()
|
||||
|
||||
val transactionsGrouping by uiSettings.transactionsGrouping.collectAsState()
|
||||
|
||||
val filterService = DI.accountTransactionsFilterService
|
||||
|
||||
val years by remember(transactions) { derivedStateOf { filterService.getYearForWhichWeHaveTransactions(transactions.value).sorted() + listOf(null as? Int) } }
|
||||
|
@ -43,6 +46,19 @@ fun FilterBar() {
|
|||
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(), verticalAlignment = Alignment.CenterVertically) {
|
||||
Text("Umsätze", Modifier.width(labelsWidth))
|
||||
|
||||
Select(
|
||||
label = "Gruppieren",
|
||||
items = TransactionsGrouping.entries,
|
||||
selectedItem = transactionsGrouping,
|
||||
onSelectedItemChanged = { grouping -> uiSettings.transactionsGrouping.value = grouping },
|
||||
getItemDisplayText = { grouping -> Internationalization.translate(grouping) },
|
||||
modifier = Modifier.width(selectBoxesWidth).padding(horizontal = horizontalPadding)
|
||||
)
|
||||
}
|
||||
|
||||
Row(Modifier.fillMaxWidth().padding(top = 8.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Text("Zeitraum", Modifier.width(labelsWidth))
|
||||
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
package net.codinux.banking.ui.composables.settings
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import net.codinux.banking.ui.config.DI
|
||||
import net.codinux.banking.ui.config.Internationalization
|
||||
import net.codinux.banking.ui.forms.BooleanOption
|
||||
import net.codinux.banking.ui.forms.Select
|
||||
import net.codinux.banking.ui.model.TransactionsGrouping
|
||||
|
||||
@Composable
|
||||
fun UiSettings(modifier: Modifier, textColor: Color = Color.Unspecified) {
|
||||
|
@ -15,7 +21,7 @@ fun UiSettings(modifier: Modifier, textColor: Color = Color.Unspecified) {
|
|||
|
||||
val showBalance by uiSettings.showBalance.collectAsState()
|
||||
|
||||
val groupTransactions by uiSettings.groupTransactions.collectAsState()
|
||||
val transactionsGrouping by uiSettings.transactionsGrouping.collectAsState()
|
||||
|
||||
val zebraStripes by uiSettings.zebraStripes.collectAsState()
|
||||
|
||||
|
@ -27,13 +33,25 @@ fun UiSettings(modifier: Modifier, textColor: Color = Color.Unspecified) {
|
|||
Column(modifier) {
|
||||
BooleanOption("Kontostand anzeigen", showBalance, textColor = textColor) { uiSettings.showBalance.value = it }
|
||||
|
||||
BooleanOption("Umsätze gruppieren", groupTransactions, textColor = textColor) { uiSettings.groupTransactions.value = it }
|
||||
|
||||
BooleanOption("Zebra Stripes", zebraStripes, textColor = textColor) { uiSettings.zebraStripes.value = it }
|
||||
|
||||
BooleanOption("Bank Icons anzeigen", showBankIcons, textColor = textColor) { uiSettings.showBankIcons.value = it }
|
||||
|
||||
BooleanOption("Umsätze farbig anzeigen", showColoredAmounts, textColor = textColor) { uiSettings.showColoredAmounts.value = it }
|
||||
|
||||
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
|
||||
Text("Umsätze gruppieren", color = textColor)
|
||||
|
||||
Select(
|
||||
label = "",
|
||||
items = TransactionsGrouping.entries,
|
||||
selectedItem = transactionsGrouping,
|
||||
onSelectedItemChanged = { grouping -> uiSettings.transactionsGrouping.value = grouping },
|
||||
getItemDisplayText = { grouping -> Internationalization.translate(grouping) },
|
||||
textColor = textColor,
|
||||
modifier = Modifier.width(175.dp).padding(horizontal = 6.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -12,12 +12,13 @@ import androidx.compose.ui.graphics.Color
|
|||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import kotlinx.datetime.LocalDate
|
||||
import net.codinux.banking.client.model.Amount
|
||||
import net.codinux.banking.dataaccess.entities.UserAccountEntity
|
||||
import net.codinux.banking.ui.config.DI
|
||||
import net.codinux.banking.ui.forms.RoundedCornersCard
|
||||
import net.codinux.banking.ui.model.AccountTransactionViewModel
|
||||
import net.codinux.banking.ui.model.TransactionsGrouping
|
||||
import net.codinux.banking.ui.service.TransactionsGroupingService
|
||||
|
||||
private val calculator = DI.calculator
|
||||
|
||||
|
@ -27,21 +28,23 @@ private val formatUtil = DI.formatUtil
|
|||
fun GroupedTransactionsListItems(
|
||||
modifier: Modifier,
|
||||
transactionsToDisplay: List<AccountTransactionViewModel>,
|
||||
userAccountsId: Map<Long, UserAccountEntity>
|
||||
userAccountsId: Map<Long, UserAccountEntity>,
|
||||
transactionsGrouping: TransactionsGrouping
|
||||
) {
|
||||
val groupingService = remember { TransactionsGroupingService() }
|
||||
|
||||
val groupedByMonth by remember(transactionsToDisplay) {
|
||||
derivedStateOf { transactionsToDisplay.groupBy { LocalDate(it.valueDate.year, it.valueDate.monthNumber, 1) } }
|
||||
val groupedByDate by remember(transactionsToDisplay, transactionsGrouping) {
|
||||
derivedStateOf { transactionsToDisplay.groupBy { groupingService.getKeyForGroup(it, transactionsGrouping) } }
|
||||
}
|
||||
|
||||
val showColoredAmounts by DI.uiSettings.showColoredAmounts.collectAsState()
|
||||
|
||||
|
||||
LazyColumn(modifier, contentPadding = PaddingValues(bottom = 12.dp)) { // padding bottom = add the space the FAB sticks into the content area (= 26 - the 16 we add at the bottom of the expenses line)
|
||||
items(groupedByMonth.keys.sortedDescending()) { month ->
|
||||
items(groupedByDate.keys.sortedDescending()) { groupingDate ->
|
||||
Column(Modifier.fillMaxWidth()) {
|
||||
Text(
|
||||
text = DI.formatUtil.formatMonth(month),
|
||||
text = DI.formatUtil.formatGroupingDate(groupingDate, transactionsGrouping),
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
modifier = Modifier.padding(top = 8.dp, bottom = 2.dp),
|
||||
|
@ -49,8 +52,7 @@ fun GroupedTransactionsListItems(
|
|||
|
||||
Spacer(Modifier.height(4.dp))
|
||||
|
||||
val monthTransactions =
|
||||
groupedByMonth[month].orEmpty().sortedByDescending { it.valueDate }
|
||||
val monthTransactions = groupedByDate[groupingDate].orEmpty().sortedByDescending { it.valueDate }
|
||||
|
||||
RoundedCornersCard {
|
||||
Column(Modifier.background(Color.White)) { // LazyColumn inside LazyColumn is not allowed
|
||||
|
|
|
@ -12,6 +12,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.unit.dp
|
||||
import net.codinux.banking.ui.config.Colors
|
||||
import net.codinux.banking.ui.config.DI
|
||||
import net.codinux.banking.ui.model.TransactionsGrouping
|
||||
import net.codinux.banking.ui.settings.UiSettings
|
||||
import net.codinux.banking.ui.state.UiState
|
||||
import org.jetbrains.compose.ui.tooling.preview.Preview
|
||||
|
@ -39,7 +40,7 @@ fun TransactionsList(uiState: UiState, uiSettings: UiSettings, isMobile: Boolean
|
|||
|
||||
val showBalance by uiSettings.showBalance.collectAsState()
|
||||
|
||||
val groupTransactions by uiSettings.groupTransactions.collectAsState()
|
||||
val transactionsGrouping by uiSettings.transactionsGrouping.collectAsState()
|
||||
|
||||
val showColoredAmounts by DI.uiSettings.showColoredAmounts.collectAsState()
|
||||
|
||||
|
@ -57,8 +58,8 @@ fun TransactionsList(uiState: UiState, uiSettings: UiSettings, isMobile: Boolean
|
|||
}
|
||||
}
|
||||
|
||||
if (groupTransactions) {
|
||||
GroupedTransactionsListItems(transactionsListModifier, transactionsToDisplay, userAccountsId)
|
||||
if (transactionsGrouping != TransactionsGrouping.None) {
|
||||
GroupedTransactionsListItems(transactionsListModifier, transactionsToDisplay, userAccountsId, transactionsGrouping)
|
||||
} else {
|
||||
LazyColumn(transactionsListModifier, contentPadding = PaddingValues(top = 8.dp, bottom = 16.dp)) {
|
||||
itemsIndexed(transactionsToDisplay) { index, transaction ->
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package net.codinux.banking.ui.config
|
||||
|
||||
import net.codinux.banking.client.model.tan.ActionRequiringTan
|
||||
import net.codinux.banking.ui.model.TransactionsGrouping
|
||||
|
||||
object Internationalization {
|
||||
|
||||
|
@ -21,4 +22,12 @@ object Internationalization {
|
|||
ActionRequiringTan.ChangeTanMedium -> "um das TAN Medium zu ändern"
|
||||
}
|
||||
|
||||
fun translate(transactionsGrouping: TransactionsGrouping): String = when (transactionsGrouping) {
|
||||
TransactionsGrouping.Quarter -> "Quartal"
|
||||
TransactionsGrouping.Month -> "Monat"
|
||||
TransactionsGrouping.Day -> "Tag"
|
||||
TransactionsGrouping.Week -> "Woche"
|
||||
TransactionsGrouping.None -> "Nicht gruppieren"
|
||||
}
|
||||
|
||||
}
|
|
@ -4,6 +4,9 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import net.codinux.banking.ui.config.Colors
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
|
@ -14,6 +17,7 @@ fun <T> Select(
|
|||
onSelectedItemChanged: (T) -> Unit,
|
||||
getItemDisplayText: (T) -> String,
|
||||
modifier: Modifier = Modifier,
|
||||
textColor: Color? = null,
|
||||
leadingIcon: @Composable (() -> Unit)? = null,
|
||||
dropDownItemContent: @Composable ((T) -> Unit)? = null
|
||||
) {
|
||||
|
@ -24,11 +28,14 @@ fun <T> Select(
|
|||
value = getItemDisplayText(selectedItem),
|
||||
onValueChange = { },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
label = { Text(label) },
|
||||
textStyle = if (textColor != null) TextStyle(textColor) else LocalTextStyle.current,
|
||||
label = { Text(label, color = textColor ?: Color.Unspecified) },
|
||||
readOnly = true,
|
||||
maxLines = 1,
|
||||
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(showDropDownMenu) },
|
||||
leadingIcon = leadingIcon
|
||||
leadingIcon = leadingIcon,
|
||||
colors = if (textColor == null) TextFieldDefaults.outlinedTextFieldColors()
|
||||
else TextFieldDefaults.outlinedTextFieldColors(textColor = textColor, unfocusedBorderColor = textColor, unfocusedLabelColor = textColor, placeholderColor = textColor, focusedBorderColor = Colors.CodinuxSecondaryColor)
|
||||
)
|
||||
|
||||
// due to a bug (still not fixed since 2021) in ExposedDropdownMenu its popup has a maximum width of 800 pixel / 320dp which is too less to fit
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package net.codinux.banking.ui.model
|
||||
|
||||
enum class TransactionsGrouping {
|
||||
None,
|
||||
Day,
|
||||
Week,
|
||||
Month,
|
||||
Quarter
|
||||
}
|
|
@ -1,16 +1,40 @@
|
|||
package net.codinux.banking.ui.service
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.Month
|
||||
import kotlinx.datetime.*
|
||||
import net.codinux.banking.client.model.Amount
|
||||
import net.codinux.banking.ui.config.Colors
|
||||
import net.codinux.banking.ui.model.TransactionsGrouping
|
||||
|
||||
class FormatUtil {
|
||||
|
||||
fun formatDate(date: LocalDate): String = // TODO: find a better way
|
||||
"${minDigits(date.dayOfMonth, 2)}.${minDigits(date.monthNumber, 2)}.${date.year.toString().substring(2)}"
|
||||
|
||||
fun formatGroupingDate(date: LocalDate, transactionsGrouping: TransactionsGrouping): String = when (transactionsGrouping) {
|
||||
TransactionsGrouping.Day -> formatDate(date)
|
||||
TransactionsGrouping.Week -> formatWeek(date)
|
||||
TransactionsGrouping.Month -> formatMonth(date)
|
||||
TransactionsGrouping.Quarter -> {
|
||||
val quarter = when (date.monthNumber) {
|
||||
1 -> "1"
|
||||
4 -> "2"
|
||||
7 -> "3"
|
||||
else -> "4"
|
||||
}
|
||||
"${quarter}. Quartal ${date.year}"
|
||||
}
|
||||
TransactionsGrouping.None -> "" // illegal state
|
||||
}
|
||||
|
||||
fun formatWeek(date: LocalDate): String {
|
||||
// not fully correct, just for a proof of concept
|
||||
val calenderWeek = date.dayOfYear / 7 + 1
|
||||
val endOfWeek = date.plus(6, DateTimeUnit.DAY)
|
||||
|
||||
return "KW ${minDigits(calenderWeek, 2)}, ${minDigits(date.dayOfMonth, 2)}.${minDigits(date.monthNumber, 2)} - ${formatDate(endOfWeek)}"
|
||||
}
|
||||
|
||||
fun formatMonth(date: LocalDate): String = // TODO: find a better way
|
||||
"${getMonthName(date.month)} ${date.year}"
|
||||
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package net.codinux.banking.ui.service
|
||||
|
||||
import kotlinx.datetime.DayOfWeek
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.Month
|
||||
import net.codinux.banking.fints.extensions.minusDays
|
||||
import net.codinux.banking.ui.model.AccountTransactionViewModel
|
||||
import net.codinux.banking.ui.model.TransactionsGrouping
|
||||
|
||||
class TransactionsGroupingService {
|
||||
|
||||
fun getQuarter(date: LocalDate): Int = when (date.month) {
|
||||
Month.JANUARY, Month.FEBRUARY, Month.MARCH -> 1
|
||||
Month.APRIL, Month.MAY, Month.JUNE -> 4
|
||||
Month.JULY, Month.AUGUST, Month.SEPTEMBER -> 7
|
||||
else -> 10
|
||||
}
|
||||
|
||||
fun getStartOfWeek(date: LocalDate): LocalDate = when (date.dayOfWeek) {
|
||||
DayOfWeek.TUESDAY -> date.minusDays(1)
|
||||
DayOfWeek.WEDNESDAY -> date.minusDays(2)
|
||||
DayOfWeek.THURSDAY -> date.minusDays(3)
|
||||
DayOfWeek.FRIDAY -> date.minusDays(4)
|
||||
DayOfWeek.SATURDAY -> date.minusDays(5)
|
||||
DayOfWeek.SUNDAY -> date.minusDays(6)
|
||||
else -> date
|
||||
}
|
||||
|
||||
fun getKeyForGroup(transaction: AccountTransactionViewModel, grouping: TransactionsGrouping) = when (grouping) {
|
||||
TransactionsGrouping.Quarter -> LocalDate(transaction.valueDate.year, getQuarter(transaction.valueDate), 1)
|
||||
TransactionsGrouping.Month -> LocalDate(transaction.valueDate.year, transaction.valueDate.monthNumber, 1)
|
||||
TransactionsGrouping.Week -> getStartOfWeek(transaction.valueDate)
|
||||
else -> transaction.valueDate
|
||||
}
|
||||
|
||||
}
|
|
@ -2,12 +2,13 @@ package net.codinux.banking.ui.settings
|
|||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import net.codinux.banking.ui.model.TransactionsGrouping
|
||||
|
||||
class UiSettings : ViewModel() {
|
||||
|
||||
val showBalance = MutableStateFlow(true)
|
||||
|
||||
val groupTransactions = MutableStateFlow(true)
|
||||
val transactionsGrouping = MutableStateFlow(TransactionsGrouping.Month)
|
||||
|
||||
val zebraStripes = MutableStateFlow(true)
|
||||
|
||||
|
|
Loading…
Reference in New Issue