Heading for Transaction and Category added

This commit is contained in:
Florian 2025-01-20 09:08:44 +01:00
parent c97e422928
commit 8fc52e576d
4 changed files with 110 additions and 22 deletions

View File

@ -2,17 +2,21 @@ package at.xaxa.ledger.ui.category.overview
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import at.xaxa.ledger.ui.AppViewModelProvider import at.xaxa.ledger.ui.AppViewModelProvider
import at.xaxa.ledger.ui.ButtonSuccess import at.xaxa.ledger.ui.ButtonSuccess
@ -24,8 +28,7 @@ fun CategoryOverview(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onCardClick: (Int) -> Unit, onCardClick: (Int) -> Unit,
categoryViewModel: OverviewCategoryViewModel = viewModel(factory = AppViewModelProvider.Factory) categoryViewModel: OverviewCategoryViewModel = viewModel(factory = AppViewModelProvider.Factory)
){ ) {
val categories by categoryViewModel.categoryUiState.categories.collectAsState(initial = emptyList()) val categories by categoryViewModel.categoryUiState.categories.collectAsState(initial = emptyList())
Column( Column(
@ -34,6 +37,16 @@ fun CategoryOverview(
.padding(16.dp, 0.dp), .padding(16.dp, 0.dp),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
// Headline: "Your Categories"
Text(
text = "Your Categories",
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.padding(vertical = 16.dp)
.align(Alignment.Start)
)
LazyColumn( LazyColumn(
Modifier.weight(1f) Modifier.weight(1f)
) { ) {

View File

@ -3,6 +3,7 @@ package at.xaxa.ledger.ui.entry.add
import android.util.Log import android.util.Log
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@ -15,6 +16,7 @@ import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Switch
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -35,6 +37,7 @@ import at.xaxa.ledger.ui.ButtonSuccess
import at.xaxa.ledger.ui.DatePickerDocked import at.xaxa.ledger.ui.DatePickerDocked
import at.xaxa.ledger.ui.category.iconNames import at.xaxa.ledger.ui.category.iconNames
import at.xaxa.ledger.ui.category.icons import at.xaxa.ledger.ui.category.icons
import kotlin.math.abs
import kotlin.math.log import kotlin.math.log
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@ -50,10 +53,11 @@ fun Add(
var spending by remember { mutableStateOf("") } var spending by remember { mutableStateOf("") }
var selectedDate by remember { mutableLongStateOf(System.currentTimeMillis()) } var selectedDate by remember { mutableLongStateOf(System.currentTimeMillis()) }
var expanded by remember { mutableStateOf(false) } var expanded by remember { mutableStateOf(false) }
var selectedIconIndex by remember { mutableStateOf(0) } // Store index of selected icon var selectedIconIndex by remember { mutableStateOf(0) }
var selectedCategoryName by remember { mutableStateOf("") } // Store index of selected icon var selectedCategoryName by remember { mutableStateOf("") }
var selectedCategory by remember { mutableIntStateOf(-1) } var selectedCategory by remember { mutableIntStateOf(-1) }
var parsingError by remember { mutableStateOf(false) } var parsingError by remember { mutableStateOf(false) }
var isIncome by remember { mutableStateOf(true) } // true for Income, false for Expense
if (parsingError) { if (parsingError) {
AlertDialog( AlertDialog(
@ -97,10 +101,14 @@ fun Add(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
) )
// Toggle between Income and Expense
OutlinedTextField( OutlinedTextField(
value = spending, value = spending,
onValueChange = { onValueChange = {
val isValidSpending = it.matches(Regex("^[+-]?\\d*(\\.\\d{0,2})?$")) val isValidSpending = it.matches(Regex("^-?\\d*(\\.\\d{0,2})?$"))
if (isValidSpending) { if (isValidSpending) {
spending = it spending = it
} }
@ -115,13 +123,13 @@ fun Add(
onExpandedChange = { expanded = it } onExpandedChange = { expanded = it }
) { ) {
OutlinedTextField( OutlinedTextField(
value = selectedCategoryName, // Show selected icon name value = selectedCategoryName,
onValueChange = {}, onValueChange = {},
label = { Text("Category") }, label = { Text("Category") },
readOnly = true, readOnly = true,
leadingIcon = { leadingIcon = {
Icon( Icon(
imageVector = icons[selectedIconIndex], // Replace with your desired icon imageVector = icons[selectedIconIndex],
contentDescription = "Leading Icon" contentDescription = "Leading Icon"
) )
}, },
@ -139,9 +147,9 @@ fun Add(
) { ) {
categories.forEachIndexed { index, category -> categories.forEachIndexed { index, category ->
DropdownMenuItem( DropdownMenuItem(
text = { Text(text = category.categoryName) }, // Use name from iconNames text = { Text(text = category.categoryName) },
onClick = { onClick = {
selectedIconIndex = category.icon // Update selected index selectedIconIndex = category.icon
expanded = false expanded = false
selectedCategoryName = category.categoryName selectedCategoryName = category.categoryName
selectedCategory = category._id selectedCategory = category._id
@ -160,8 +168,29 @@ fun Add(
DatePickerDocked { dateMilis -> DatePickerDocked { dateMilis ->
selectedDate = dateMilis selectedDate = dateMilis
} }
Row(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(text = if (isIncome) "Income" else "Expense")
Switch(
modifier = Modifier.padding(8.dp, 0.dp),
checked = isIncome,
onCheckedChange = {
isIncome = it
if(isIncome){
spending = abs(spending.toFloat()).toString()
}else{
spending = (abs(spending.toFloat())*-1f).toString()
}
}
)
}
} }
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth(), .fillMaxWidth(),
@ -171,8 +200,7 @@ fun Add(
modifier = Modifier, modifier = Modifier,
"Add Transaction", "Add Transaction",
onClick = { onClick = {
val isValidSpending = spending.matches(Regex("^[+-]?\\d*(\\.\\d{0,2})?$")) val isValidSpending = spending.matches(Regex("^-?\\d*(\\.\\d{0,2})?$"))
if (name.isNotBlank() && isValidSpending && selectedDate != 0L && selectedCategory != -1) { if (name.isNotBlank() && isValidSpending && selectedDate != 0L && selectedCategory != -1) {
val newEntry = Entry( val newEntry = Entry(
id = 0, id = 0,
@ -184,10 +212,10 @@ fun Add(
addViewModel.addEntryToDB(newEntry) addViewModel.addEntryToDB(newEntry)
onCardClick() onCardClick()
} else { } else {
parsingError = true; parsingError = true
} }
} }
) )
} }
} }
} }

View File

@ -2,6 +2,7 @@ package at.xaxa.ledger.ui.entry.edit
import android.util.Log import android.util.Log
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@ -12,6 +13,7 @@ import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.ExposedDropdownMenuDefaults.TrailingIcon import androidx.compose.material3.ExposedDropdownMenuDefaults.TrailingIcon
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Switch
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@ -32,6 +34,7 @@ import at.xaxa.ledger.ui.ButtonSuccess
import at.xaxa.ledger.ui.DatePickerDocked import at.xaxa.ledger.ui.DatePickerDocked
import at.xaxa.ledger.ui.category.iconNames import at.xaxa.ledger.ui.category.iconNames
import at.xaxa.ledger.ui.category.icons import at.xaxa.ledger.ui.category.icons
import kotlin.math.abs
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@ -48,16 +51,16 @@ fun Edit(
var selectedIconIndex by remember { mutableStateOf(category.icon) } var selectedIconIndex by remember { mutableStateOf(category.icon) }
var selectedCategoryName by remember { mutableStateOf(category.categoryName) } var selectedCategoryName by remember { mutableStateOf(category.categoryName) }
var selectedCategory by remember { mutableIntStateOf(category._id) } var selectedCategory by remember { mutableIntStateOf(category._id) }
var expanded by remember { mutableStateOf(false) } var expanded by remember { mutableStateOf(false) }
var isIncome by remember { mutableStateOf(entry.amount >= 0) } // true for Income, false for Expense
LaunchedEffect(category) { LaunchedEffect(category) {
selectedIconIndex = category.icon selectedIconIndex = category.icon
selectedCategoryName = category.categoryName selectedCategoryName = category.categoryName
selectedCategory = category._id selectedCategory = category._id
isIncome = (entry.amount >= 0)
} }
Column( Column(
modifier = modifier modifier = modifier
.fillMaxSize() .fillMaxSize()
@ -73,12 +76,17 @@ fun Edit(
label = { Text("Name") }, label = { Text("Name") },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
// Toggle between Income and Expense
OutlinedTextField( OutlinedTextField(
value = entry.amount.toString(), value = entry.amount.toString(),
onValueChange = { onValueChange = {
val isValidSpending = it.matches(Regex("^[+-]?\\d*(\\.\\d{0,2})?$")) val isValidSpending = it.matches(Regex("^-?\\d*(\\.\\d{0,2})?$"))
if (isValidSpending) { if (isValidSpending) {
editViewModel.updateEntry(entry.copy(amount = it.toFloat())) val amount = if (isIncome) it.toFloat() else -it.toFloat()
editViewModel.updateEntry(entry.copy(amount = amount))
} }
}, },
label = { Text("Amount") }, label = { Text("Amount") },
@ -90,13 +98,13 @@ fun Edit(
onExpandedChange = { expanded = it } onExpandedChange = { expanded = it }
) { ) {
OutlinedTextField( OutlinedTextField(
value = selectedCategoryName, // Show selected category name value = selectedCategoryName,
onValueChange = {}, onValueChange = {},
label = { Text("Category") }, label = { Text("Category") },
readOnly = true, readOnly = true,
leadingIcon = { leadingIcon = {
Icon( Icon(
imageVector = icons[selectedIconIndex], // Show selected icon imageVector = icons[selectedIconIndex],
contentDescription = "Leading Icon" contentDescription = "Leading Icon"
) )
}, },
@ -116,9 +124,9 @@ fun Edit(
DropdownMenuItem( DropdownMenuItem(
text = { Text(text = category.categoryName) }, text = { Text(text = category.categoryName) },
onClick = { onClick = {
selectedIconIndex = category.icon // Update selected icon index selectedIconIndex = category.icon
selectedCategoryName = category.categoryName // Update selected category name selectedCategoryName = category.categoryName
selectedCategory = category._id // Update selected category ID selectedCategory = category._id
expanded = false expanded = false
}, },
leadingIcon = { leadingIcon = {
@ -135,8 +143,30 @@ fun Edit(
DatePickerDocked(entry) { dateMilis -> DatePickerDocked(entry) { dateMilis ->
editViewModel.updateEntry(entry.copy(date = dateMilis)) editViewModel.updateEntry(entry.copy(date = dateMilis))
} }
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(text = if (isIncome) "Income" else "Expense")
Switch(
modifier = Modifier.padding(8.dp, 0.dp),
checked = isIncome,
onCheckedChange = {
isIncome = it
if(isIncome){
editViewModel.updateEntry(entry.copy(amount = abs(entry.amount)))
}else{
editViewModel.updateEntry(entry.copy(amount = abs(entry.amount)*-1f))
}
}
)
}
} }
Column( Column(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
@ -155,6 +185,7 @@ fun Edit(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
text = "Done", text = "Done",
onClick = { onClick = {
editViewModel.updateEntry(entry)
editViewModel.saveEntry() editViewModel.saveEntry()
onCardClick() onCardClick()
} }

View File

@ -10,12 +10,15 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import at.xaxa.ledger.ui.AppViewModelProvider import at.xaxa.ledger.ui.AppViewModelProvider
import at.xaxa.ledger.ui.ButtonSuccess import at.xaxa.ledger.ui.ButtonSuccess
@ -37,18 +40,31 @@ fun Home(modifier: Modifier = Modifier, onCardClick: (Int) -> Unit, onButtonClic
.padding(16.dp, 0.dp), .padding(16.dp, 0.dp),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
LazyColumn( LazyColumn(
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
) { ) {
stickyHeader { stickyHeader {
HeaderCard(modifier = modifier, "%.2f".format(balance)+"", onCatButtonClick) HeaderCard(modifier = modifier, "%.2f".format(balance)+"", onCatButtonClick)
Text(
text = "Your Transactions",
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.padding(vertical = 16.dp)
.align(Alignment.Start)
)
} }
items(state) { item -> items(state) { item ->
Column( Column(
modifier = Modifier.padding(vertical = 4.dp) modifier = Modifier.padding(vertical = 4.dp)
) { ) {
HorizontalCard( HorizontalCard(
modifier = modifier, modifier = modifier,
name = item.name, name = item.name,