From f246d2efbbc0acb09a41211a3df514827fe55858 Mon Sep 17 00:00:00 2001 From: Florian Date: Wed, 15 Jan 2025 10:48:36 +0100 Subject: [PATCH 1/3] .. --- .../ledger/ui/category/CategoryViewModel.kt | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/Ledger/app/src/main/java/at/xaxa/ledger/ui/category/CategoryViewModel.kt b/Ledger/app/src/main/java/at/xaxa/ledger/ui/category/CategoryViewModel.kt index e1eff0e..2aee3a9 100644 --- a/Ledger/app/src/main/java/at/xaxa/ledger/ui/category/CategoryViewModel.kt +++ b/Ledger/app/src/main/java/at/xaxa/ledger/ui/category/CategoryViewModel.kt @@ -1,2 +1,61 @@ package at.xaxa.ledger.ui.category +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import at.xaxa.ledger.data.Entry +import at.xaxa.ledger.data.EntryRepository +import at.xaxa.ledger.data.db.Category.CategoryEntity +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + + +data class CategoryListUIState(val categories: Flow> = flowOf(emptyList())) + + +class CategoryViewModel( + private val savedStateHandle: SavedStateHandle, + private val repository: EntryRepository +) : ViewModel() { + var categoryUiState by mutableStateOf(CategoryListUIState()) + + init { + getAllCategories() + } + + fun addCategory(category: CategoryEntity) { + viewModelScope.launch { + try { + val categoryEntity = CategoryEntity( + _id = category._id, + categoryName = category.categoryName, + icon = category.icon, + ) + + withContext(Dispatchers.IO) { + repository.insertCategory(categoryEntity) // Add game to the database + } + // Optionally, log or update UI state to reflect that the game was added + } catch (e: Exception) { + } + } + } + + fun getAllCategories() { + viewModelScope.launch { + val categories = withContext(Dispatchers.IO) { + repository.getAllCategories() + } + categoryUiState = CategoryListUIState(categories) + + } + + } +} From f585e0c01c1456167eb4eaed94aa09ccaf9f219c Mon Sep 17 00:00:00 2001 From: Florian Date: Wed, 15 Jan 2025 14:52:42 +0100 Subject: [PATCH 2/3] addCategory added and semifunctional --- .../main/java/at/xaxa/ledger/data/Entry.kt | 2 +- .../at/xaxa/ledger/ui/AppViewModelProvider.kt | 4 + .../main/java/at/xaxa/ledger/ui/LedgerApp.kt | 17 +++- .../at/xaxa/ledger/ui/category/CategoryUI.kt | 90 +++++++++++++++++++ .../java/at/xaxa/ledger/ui/edit/EditUI.kt | 10 ++- .../at/xaxa/ledger/ui/edit/EditViewModel.kt | 3 +- .../java/at/xaxa/ledger/ui/home/HomeUI.kt | 17 +++- 7 files changed, 135 insertions(+), 8 deletions(-) diff --git a/Ledger/app/src/main/java/at/xaxa/ledger/data/Entry.kt b/Ledger/app/src/main/java/at/xaxa/ledger/data/Entry.kt index 0d7e11b..7430ee6 100644 --- a/Ledger/app/src/main/java/at/xaxa/ledger/data/Entry.kt +++ b/Ledger/app/src/main/java/at/xaxa/ledger/data/Entry.kt @@ -2,7 +2,7 @@ package at.xaxa.ledger.data import kotlinx.coroutines.flow.Flow -class Entry ( +data class Entry ( val id: Int, val name: String, val amount: Float, diff --git a/Ledger/app/src/main/java/at/xaxa/ledger/ui/AppViewModelProvider.kt b/Ledger/app/src/main/java/at/xaxa/ledger/ui/AppViewModelProvider.kt index 26cc562..72b1cd7 100644 --- a/Ledger/app/src/main/java/at/xaxa/ledger/ui/AppViewModelProvider.kt +++ b/Ledger/app/src/main/java/at/xaxa/ledger/ui/AppViewModelProvider.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory import at.xaxa.ledger.LedgerApplication import at.xaxa.ledger.ui.add.AddViewModel +import at.xaxa.ledger.ui.category.CategoryViewModel import at.xaxa.ledger.ui.edit.EditViewModel import at.xaxa.ledger.ui.home.HomeViewModel @@ -23,5 +24,8 @@ object AppViewModelProvider { initializer { EditViewModel(this.createSavedStateHandle(), (this[APPLICATION_KEY] as LedgerApplication).entryRepository) } + initializer { + CategoryViewModel(this.createSavedStateHandle(), (this[APPLICATION_KEY] as LedgerApplication).entryRepository) + } } } \ No newline at end of file diff --git a/Ledger/app/src/main/java/at/xaxa/ledger/ui/LedgerApp.kt b/Ledger/app/src/main/java/at/xaxa/ledger/ui/LedgerApp.kt index e78b0cb..7fc5d75 100644 --- a/Ledger/app/src/main/java/at/xaxa/ledger/ui/LedgerApp.kt +++ b/Ledger/app/src/main/java/at/xaxa/ledger/ui/LedgerApp.kt @@ -13,13 +13,15 @@ import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument import at.xaxa.ledger.ui.add.Add +import at.xaxa.ledger.ui.category.addCategory import at.xaxa.ledger.ui.edit.Edit import at.xaxa.ledger.ui.home.Home enum class AppRoutes(val route: String) { Home("home"), Add("add"), - Edit("edit/{entryId}") + Edit("edit/{entryId}"), + Category("category") } @Composable @@ -41,6 +43,9 @@ fun LedgerApp(modifier: Modifier = Modifier){ }, onButtonClick = { navController.navigate("add") + }, + onCatButtonClick = { + navController.navigate("category") } ) } @@ -64,6 +69,14 @@ fun LedgerApp(modifier: Modifier = Modifier){ } ) } + composable(AppRoutes.Category.route){ + addCategory( + onButtonClick = { + navController.navigate("home") + } + ) + } } } -} \ No newline at end of file +} + diff --git a/Ledger/app/src/main/java/at/xaxa/ledger/ui/category/CategoryUI.kt b/Ledger/app/src/main/java/at/xaxa/ledger/ui/category/CategoryUI.kt index e1eff0e..4b79a60 100644 --- a/Ledger/app/src/main/java/at/xaxa/ledger/ui/category/CategoryUI.kt +++ b/Ledger/app/src/main/java/at/xaxa/ledger/ui/category/CategoryUI.kt @@ -1,2 +1,92 @@ package at.xaxa.ledger.ui.category +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import at.xaxa.ledger.data.db.Category.CategoryEntity +import at.xaxa.ledger.ui.AppViewModelProvider +import at.xaxa.ledger.ui.ButtonSuccess + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun addCategory( + onButtonClick: () -> Unit, + modifier: Modifier = Modifier, + categoryViewModel: CategoryViewModel = viewModel(factory = AppViewModelProvider.Factory) +) { + var name by remember { mutableStateOf("") } + var icon by remember { mutableStateOf(0) } + + val categories by categoryViewModel.categoryUiState.categories.collectAsState(initial = emptyList()) + + var expanded by remember { mutableStateOf(false) } + + + Column( + modifier = modifier + .fillMaxSize() + .padding(16.dp, 0.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Column( + Modifier.weight(1f) + ) { + OutlinedTextField( + value = name, + onValueChange = { name = it }, + label = { Text("Name") }, + modifier = Modifier + .fillMaxWidth() + ) + OutlinedTextField( + value = icon.toString(), + onValueChange = { icon = it.toInt() }, + label = { Text("Icon") }, + modifier = Modifier + .fillMaxWidth() + ) + + + Box( + modifier = Modifier + .fillMaxWidth(), + contentAlignment = Alignment.Center + ) { + ButtonSuccess( + modifier = Modifier, + "Add Category", + onClick = { + if (name.isNotBlank()) { + val newCategory = CategoryEntity( + _id = 0, + categoryName = name, + icon = icon + ) + categoryViewModel.addCategory(newCategory) + onButtonClick() + } + } + ) + } + } + } +} + + + + diff --git a/Ledger/app/src/main/java/at/xaxa/ledger/ui/edit/EditUI.kt b/Ledger/app/src/main/java/at/xaxa/ledger/ui/edit/EditUI.kt index db53b4e..47be8da 100644 --- a/Ledger/app/src/main/java/at/xaxa/ledger/ui/edit/EditUI.kt +++ b/Ledger/app/src/main/java/at/xaxa/ledger/ui/edit/EditUI.kt @@ -23,6 +23,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import at.xaxa.ledger.data.Entry import at.xaxa.ledger.ui.AppViewModelProvider import at.xaxa.ledger.ui.ButtonDanger import at.xaxa.ledger.ui.ButtonSuccess @@ -30,7 +31,8 @@ import at.xaxa.ledger.ui.DatePickerDocked @OptIn(ExperimentalMaterial3Api::class) @Composable -fun Edit(modifier: Modifier = Modifier, onCardClick: () -> Unit, editViewModel : EditViewModel = viewModel(factory = AppViewModelProvider.Factory)) { +fun Edit(modifier: Modifier = Modifier, onCardClick: () -> Unit, editViewModel : EditViewModel = viewModel(factory = AppViewModelProvider.Factory), onValueChange: (Entry) -> Unit = {}, +) { val entry = editViewModel.editUiState.entry val categories by editViewModel.categoryUiState.categories.collectAsState(initial = emptyList()) @@ -57,8 +59,10 @@ fun Edit(modifier: Modifier = Modifier, onCardClick: () -> Unit, editViewModel : Modifier.weight(1f) ) { OutlinedTextField( - value = name, - onValueChange = { name = it }, + value = entry.name, + onValueChange = { newText -> + onValueChange(entry.copy(name = newText)) + }, label = { Text("Name") }, modifier = Modifier .fillMaxWidth() diff --git a/Ledger/app/src/main/java/at/xaxa/ledger/ui/edit/EditViewModel.kt b/Ledger/app/src/main/java/at/xaxa/ledger/ui/edit/EditViewModel.kt index dcc8496..9350aac 100644 --- a/Ledger/app/src/main/java/at/xaxa/ledger/ui/edit/EditViewModel.kt +++ b/Ledger/app/src/main/java/at/xaxa/ledger/ui/edit/EditViewModel.kt @@ -21,7 +21,7 @@ data class CategoryUIState(val categories: CategoryEntity = CategoryEntity(0,"", data class EditUI( - val entry: Entry = Entry(0, "", 0.0f, 0, 0) + val entry: Entry = Entry(0, "AS", 0.0f, 0, 0) ) class EditViewModel(private val savedStateHandle: SavedStateHandle, @@ -31,6 +31,7 @@ class EditViewModel(private val savedStateHandle: SavedStateHandle, var editUiState by mutableStateOf(EditUI()) + private set var categoryUiState by mutableStateOf(CategoryListUIState()) var categoryUi by mutableStateOf(CategoryUIState()) 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 618f2cb..9b300ce 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 @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth 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.runtime.Composable @@ -23,7 +24,7 @@ import at.xaxa.ledger.ui.convertMillisToDate @OptIn(ExperimentalFoundationApi::class) @Composable -fun Home(modifier: Modifier = Modifier, onCardClick: (Int) -> Unit, onButtonClick: () -> Unit, HomeViewModel : HomeViewModel = viewModel(factory = AppViewModelProvider.Factory)) { +fun Home(modifier: Modifier = Modifier, onCardClick: (Int) -> Unit, onButtonClick: () -> Unit, onCatButtonClick: () -> Unit, HomeViewModel : HomeViewModel = viewModel(factory = AppViewModelProvider.Factory)) { val state by HomeViewModel.entryUIState.entry.collectAsState(initial = emptyList()) Column( @@ -39,6 +40,20 @@ fun Home(modifier: Modifier = Modifier, onCardClick: (Int) -> Unit, onButtonClic ) { stickyHeader { HeaderCard(modifier = modifier, "-13563.00€") + Box( + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp), // Add slight padding from the header + contentAlignment = Alignment.TopStart // Align button to the top-left + ) { + ButtonSuccess( + modifier = Modifier + .width(120.dp) // Make the button smaller + .padding(4.dp), // Add some padding for better touch area + text = "Add Category", + onClick = { onCatButtonClick() } + ) + } } items(state) { item -> Column( From adcd8bb3a03f6ecc6bd0bea39f38b396de5b22ac Mon Sep 17 00:00:00 2001 From: Florian Date: Wed, 15 Jan 2025 15:26:52 +0100 Subject: [PATCH 3/3] CategoryUI updated --- .../main/java/at/xaxa/ledger/ui/LedgerUI.kt | 21 +++++++++++++ .../at/xaxa/ledger/ui/category/CategoryUI.kt | 30 ++++++++++++++----- .../ledger/ui/category/CategoryViewModel.kt | 12 +++++++- 3 files changed, 55 insertions(+), 8 deletions(-) diff --git a/Ledger/app/src/main/java/at/xaxa/ledger/ui/LedgerUI.kt b/Ledger/app/src/main/java/at/xaxa/ledger/ui/LedgerUI.kt index 48b35a9..1e6138c 100644 --- a/Ledger/app/src/main/java/at/xaxa/ledger/ui/LedgerUI.kt +++ b/Ledger/app/src/main/java/at/xaxa/ledger/ui/LedgerUI.kt @@ -141,6 +141,27 @@ fun HorizontalCard(modifier: Modifier = Modifier, name: String, date: String, am } } +@Composable +fun CategoryCard(modifier: Modifier = Modifier, name: String, icon : Int, onClick: () -> Unit ) { + Surface( + onClick = onClick, + shape = RoundedCornerShape(12.dp), + color = Color(0xfff9f9f9), + border = BorderStroke(1.dp, Color(0xffc6c6c6)), + modifier = modifier + .clip(shape = RoundedCornerShape(12.dp)) + ) { + Row( + modifier = Modifier + .fillMaxSize() + .padding(16.dp) + ) { + LayoutMediaText(modifier, name, date, amount) + } + } +} + + @Composable fun LayoutMediaText(modifier: Modifier = Modifier, name: String, date: String, amount:String) { Row( diff --git a/Ledger/app/src/main/java/at/xaxa/ledger/ui/category/CategoryUI.kt b/Ledger/app/src/main/java/at/xaxa/ledger/ui/category/CategoryUI.kt index 4b79a60..5f91e6b 100644 --- a/Ledger/app/src/main/java/at/xaxa/ledger/ui/category/CategoryUI.kt +++ b/Ledger/app/src/main/java/at/xaxa/ledger/ui/category/CategoryUI.kt @@ -5,6 +5,8 @@ import androidx.compose.foundation.layout.Column 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.ExperimentalMaterial3Api import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text @@ -21,6 +23,9 @@ import androidx.lifecycle.viewmodel.compose.viewModel import at.xaxa.ledger.data.db.Category.CategoryEntity import at.xaxa.ledger.ui.AppViewModelProvider import at.xaxa.ledger.ui.ButtonSuccess +import at.xaxa.ledger.ui.CategoryCard +import at.xaxa.ledger.ui.HorizontalCard +import at.xaxa.ledger.ui.convertMillisToDate @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -33,7 +38,7 @@ fun addCategory( var icon by remember { mutableStateOf(0) } val categories by categoryViewModel.categoryUiState.categories.collectAsState(initial = emptyList()) - + val category = categoryViewModel.categoryUi.category var expanded by remember { mutableStateOf(false) } @@ -43,25 +48,23 @@ fun addCategory( .padding(16.dp, 0.dp), horizontalAlignment = Alignment.CenterHorizontally ) { - Column( + LazyColumn( Modifier.weight(1f) ) { OutlinedTextField( - value = name, + value = category.categoryName, onValueChange = { name = it }, label = { Text("Name") }, modifier = Modifier .fillMaxWidth() ) OutlinedTextField( - value = icon.toString(), - onValueChange = { icon = it.toInt() }, + value = category.icon.toString(), + onValueChange = { category.copy(icon = it.toInt()) }, label = { Text("Icon") }, modifier = Modifier .fillMaxWidth() ) - - Box( modifier = Modifier .fillMaxWidth(), @@ -83,6 +86,19 @@ fun addCategory( } ) } + + items(categories) { item -> + Column( + modifier = Modifier.padding(vertical = 4.dp) + ) { + CategoryCard( + modifier = modifier, + name = category.categoryName, + icon = category.icon, + onClick = { onCardClick(category._id) } + ) + } + } } } } diff --git a/Ledger/app/src/main/java/at/xaxa/ledger/ui/category/CategoryViewModel.kt b/Ledger/app/src/main/java/at/xaxa/ledger/ui/category/CategoryViewModel.kt index 2aee3a9..af555ac 100644 --- a/Ledger/app/src/main/java/at/xaxa/ledger/ui/category/CategoryViewModel.kt +++ b/Ledger/app/src/main/java/at/xaxa/ledger/ui/category/CategoryViewModel.kt @@ -16,7 +16,7 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.launch import kotlinx.coroutines.withContext - +data class CategoryUIState(val category: CategoryEntity = CategoryEntity(0,"",0)) data class CategoryListUIState(val categories: Flow> = flowOf(emptyList())) @@ -25,6 +25,8 @@ class CategoryViewModel( private val repository: EntryRepository ) : ViewModel() { var categoryUiState by mutableStateOf(CategoryListUIState()) + var categoryUi by mutableStateOf(CategoryUIState()) + private set init { getAllCategories() @@ -56,6 +58,14 @@ class CategoryViewModel( categoryUiState = CategoryListUIState(categories) } + } + fun findCategoryByID(categoryId: Int) { + viewModelScope.launch { + val category = withContext(Dispatchers.IO) { + repository.findCategoryById(categoryId) + } + categoryUi = CategoryUIState(category) + } } }