diff --git a/Ledger/app/src/main/java/at/xaxa/ledger/ui/category/overview/CategoryOverview.kt b/Ledger/app/src/main/java/at/xaxa/ledger/ui/category/overview/CategoryOverview.kt index edbd456..b49665c 100644 --- a/Ledger/app/src/main/java/at/xaxa/ledger/ui/category/overview/CategoryOverview.kt +++ b/Ledger/app/src/main/java/at/xaxa/ledger/ui/category/overview/CategoryOverview.kt @@ -2,17 +2,21 @@ package at.xaxa.ledger.ui.category.overview import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.material3.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.text.font.FontWeight import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.lifecycle.viewmodel.compose.viewModel import at.xaxa.ledger.ui.AppViewModelProvider import at.xaxa.ledger.ui.ButtonSuccess @@ -24,8 +28,7 @@ fun CategoryOverview( modifier: Modifier = Modifier, onCardClick: (Int) -> Unit, categoryViewModel: OverviewCategoryViewModel = viewModel(factory = AppViewModelProvider.Factory) -){ - +) { val categories by categoryViewModel.categoryUiState.categories.collectAsState(initial = emptyList()) Column( @@ -34,6 +37,16 @@ fun CategoryOverview( .padding(16.dp, 0.dp), 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( Modifier.weight(1f) ) { diff --git a/Ledger/app/src/main/java/at/xaxa/ledger/ui/entry/add/AddUI.kt b/Ledger/app/src/main/java/at/xaxa/ledger/ui/entry/add/AddUI.kt index a13178e..b57fad6 100644 --- a/Ledger/app/src/main/java/at/xaxa/ledger/ui/entry/add/AddUI.kt +++ b/Ledger/app/src/main/java/at/xaxa/ledger/ui/entry/add/AddUI.kt @@ -3,6 +3,7 @@ package at.xaxa.ledger.ui.entry.add import android.util.Log import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -15,6 +16,7 @@ import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.Icon import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.material3.TextButton 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.category.iconNames import at.xaxa.ledger.ui.category.icons +import kotlin.math.abs import kotlin.math.log @OptIn(ExperimentalMaterial3Api::class) @@ -50,10 +53,11 @@ fun Add( var spending by remember { mutableStateOf("") } var selectedDate by remember { mutableLongStateOf(System.currentTimeMillis()) } var expanded by remember { mutableStateOf(false) } - var selectedIconIndex by remember { mutableStateOf(0) } // Store index of selected icon - var selectedCategoryName by remember { mutableStateOf("") } // Store index of selected icon + var selectedIconIndex by remember { mutableStateOf(0) } + var selectedCategoryName by remember { mutableStateOf("") } var selectedCategory by remember { mutableIntStateOf(-1) } var parsingError by remember { mutableStateOf(false) } + var isIncome by remember { mutableStateOf(true) } // true for Income, false for Expense if (parsingError) { AlertDialog( @@ -97,10 +101,14 @@ fun Add( modifier = Modifier .fillMaxWidth() ) + + // Toggle between Income and Expense + + OutlinedTextField( value = spending, onValueChange = { - val isValidSpending = it.matches(Regex("^[+-]?\\d*(\\.\\d{0,2})?$")) + val isValidSpending = it.matches(Regex("^-?\\d*(\\.\\d{0,2})?$")) if (isValidSpending) { spending = it } @@ -115,13 +123,13 @@ fun Add( onExpandedChange = { expanded = it } ) { OutlinedTextField( - value = selectedCategoryName, // Show selected icon name + value = selectedCategoryName, onValueChange = {}, label = { Text("Category") }, readOnly = true, leadingIcon = { Icon( - imageVector = icons[selectedIconIndex], // Replace with your desired icon + imageVector = icons[selectedIconIndex], contentDescription = "Leading Icon" ) }, @@ -139,9 +147,9 @@ fun Add( ) { categories.forEachIndexed { index, category -> DropdownMenuItem( - text = { Text(text = category.categoryName) }, // Use name from iconNames + text = { Text(text = category.categoryName) }, onClick = { - selectedIconIndex = category.icon // Update selected index + selectedIconIndex = category.icon expanded = false selectedCategoryName = category.categoryName selectedCategory = category._id @@ -160,8 +168,29 @@ fun Add( DatePickerDocked { 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( modifier = Modifier .fillMaxWidth(), @@ -171,8 +200,7 @@ fun Add( modifier = Modifier, "Add Transaction", 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) { val newEntry = Entry( id = 0, @@ -184,10 +212,10 @@ fun Add( addViewModel.addEntryToDB(newEntry) onCardClick() } else { - parsingError = true; + parsingError = true } } ) } } -} +} \ No newline at end of file diff --git a/Ledger/app/src/main/java/at/xaxa/ledger/ui/entry/edit/EditUI.kt b/Ledger/app/src/main/java/at/xaxa/ledger/ui/entry/edit/EditUI.kt index 8260d81..c7a2fab 100644 --- a/Ledger/app/src/main/java/at/xaxa/ledger/ui/entry/edit/EditUI.kt +++ b/Ledger/app/src/main/java/at/xaxa/ledger/ui/entry/edit/EditUI.kt @@ -2,6 +2,7 @@ package at.xaxa.ledger.ui.entry.edit import android.util.Log import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth 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.Icon import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.runtime.Composable 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.category.iconNames import at.xaxa.ledger.ui.category.icons +import kotlin.math.abs @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -48,16 +51,16 @@ fun Edit( var selectedIconIndex by remember { mutableStateOf(category.icon) } var selectedCategoryName by remember { mutableStateOf(category.categoryName) } var selectedCategory by remember { mutableIntStateOf(category._id) } - var expanded by remember { mutableStateOf(false) } + var isIncome by remember { mutableStateOf(entry.amount >= 0) } // true for Income, false for Expense LaunchedEffect(category) { selectedIconIndex = category.icon selectedCategoryName = category.categoryName selectedCategory = category._id + isIncome = (entry.amount >= 0) } - Column( modifier = modifier .fillMaxSize() @@ -73,12 +76,17 @@ fun Edit( label = { Text("Name") }, modifier = Modifier.fillMaxWidth() ) + + // Toggle between Income and Expense + + OutlinedTextField( value = entry.amount.toString(), onValueChange = { - val isValidSpending = it.matches(Regex("^[+-]?\\d*(\\.\\d{0,2})?$")) + val isValidSpending = it.matches(Regex("^-?\\d*(\\.\\d{0,2})?$")) 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") }, @@ -90,13 +98,13 @@ fun Edit( onExpandedChange = { expanded = it } ) { OutlinedTextField( - value = selectedCategoryName, // Show selected category name + value = selectedCategoryName, onValueChange = {}, label = { Text("Category") }, readOnly = true, leadingIcon = { Icon( - imageVector = icons[selectedIconIndex], // Show selected icon + imageVector = icons[selectedIconIndex], contentDescription = "Leading Icon" ) }, @@ -116,9 +124,9 @@ fun Edit( DropdownMenuItem( text = { Text(text = category.categoryName) }, onClick = { - selectedIconIndex = category.icon // Update selected icon index - selectedCategoryName = category.categoryName // Update selected category name - selectedCategory = category._id // Update selected category ID + selectedIconIndex = category.icon + selectedCategoryName = category.categoryName + selectedCategory = category._id expanded = false }, leadingIcon = { @@ -135,8 +143,30 @@ fun Edit( DatePickerDocked(entry) { 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( modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally @@ -155,6 +185,7 @@ fun Edit( modifier = Modifier.fillMaxWidth(), text = "Done", onClick = { + editViewModel.updateEntry(entry) editViewModel.saveEntry() onCardClick() } diff --git a/Ledger/app/src/main/java/at/xaxa/ledger/ui/home/HomeUI.kt b/Ledger/app/src/main/java/at/xaxa/ledger/ui/home/HomeUI.kt index 25c9625..65d54ef 100644 --- a/Ledger/app/src/main/java/at/xaxa/ledger/ui/home/HomeUI.kt +++ b/Ledger/app/src/main/java/at/xaxa/ledger/ui/home/HomeUI.kt @@ -10,12 +10,15 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.material3.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.text.font.FontWeight import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.lifecycle.viewmodel.compose.viewModel import at.xaxa.ledger.ui.AppViewModelProvider import at.xaxa.ledger.ui.ButtonSuccess @@ -37,18 +40,31 @@ fun Home(modifier: Modifier = Modifier, onCardClick: (Int) -> Unit, onButtonClic .padding(16.dp, 0.dp), horizontalAlignment = Alignment.CenterHorizontally ) { + LazyColumn( modifier = Modifier .weight(1f) ) { + stickyHeader { 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 -> Column( modifier = Modifier.padding(vertical = 4.dp) ) { + HorizontalCard( modifier = modifier, name = item.name,