From 1f6d125571389fa3b219bf0eee6584e88763ba26 Mon Sep 17 00:00:00 2001 From: dovicrad <dovicrad@fel.cvut.cz> Date: Tue, 16 Apr 2024 19:42:32 +0200 Subject: [PATCH 01/12] :cooking: add transaction database and handeling --- .idea/deploymentTargetDropDown.xml | 3 + .idea/misc.xml | 1 - app/build.gradle.kts | 21 +- .../ExampleInstrumentedTest.kt | 6 +- app/src/main/AndroidManifest.xml | 4 +- .../financemanagement/FinanceManagementApp.kt | 2 +- .../FinanceManagementApplication.kt | 13 ++ .../fel/pda/financemanagement/MainActivity.kt | 7 - .../database/OfflineTransactionRepository.kt | 12 ++ .../financemanagement/database/Transaction.kt | 15 ++ .../database/TransactionDao.kt | 34 ++++ .../database/TransactionDatabase.kt | 34 ++++ .../database/TransactionRepository.kt | 12 ++ .../database/typeConvertion/Converters.kt | 16 ++ .../pda/financemanagement/di/AppContainer.kt | 16 ++ .../model/TransactionCategory.kt | 17 ++ .../financemanagement/model/common/Result.kt | 16 ++ .../ui/components/BottomNavigation.kt | 2 +- .../ui/components/CommonScreenForState.kt | 47 +++++ .../financemanagement/ui/components/TopBar.kt | 2 +- .../ui/screens/TransactionsScreen.kt | 125 ------------ .../transactions/TransactionViewModel.kt | 65 +++++++ .../transactions/TransactionsScreen.kt | 184 ++++++++++++++++++ .../ui/utils/AppViewModelProvider.kt | 21 ++ .../pda/financemanagement/ExampleUnitTest.kt | 3 +- build.gradle.kts | 1 + gradle/libs.versions.toml | 25 ++- 27 files changed, 554 insertions(+), 150 deletions(-) create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApplication.kt create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/database/OfflineTransactionRepository.kt create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/database/Transaction.kt create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/database/TransactionDao.kt create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/database/TransactionDatabase.kt create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/database/TransactionRepository.kt create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/database/typeConvertion/Converters.kt create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/di/AppContainer.kt create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/model/TransactionCategory.kt create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/model/common/Result.kt create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/CommonScreenForState.kt delete mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/TransactionsScreen.kt create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionViewModel.kt create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/utils/AppViewModelProvider.kt diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml index 0c0c338..9c96b2b 100644 --- a/.idea/deploymentTargetDropDown.xml +++ b/.idea/deploymentTargetDropDown.xml @@ -2,6 +2,9 @@ <project version="4"> <component name="deploymentTargetDropDown"> <value> + <entry key="MainActivity"> + <State /> + </entry> <entry key="app"> <State /> </entry> diff --git a/.idea/misc.xml b/.idea/misc.xml index 0ad17cb..8978d23 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ -<?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK"> diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c5ae397..befa91c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,6 +1,7 @@ plugins { alias(libs.plugins.androidApplication) alias(libs.plugins.jetbrainsKotlinAndroid) + alias(libs.plugins.ksp) } android { @@ -50,7 +51,6 @@ android { } dependencies { - implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.activity.compose) @@ -59,8 +59,25 @@ dependencies { implementation(libs.androidx.ui.graphics) implementation(libs.androidx.ui.tooling.preview) implementation(libs.androidx.material3) - implementation(libs.androidx.navigation.runtime.ktx) implementation(libs.androidx.navigation.compose) + implementation(libs.androidx.lifecycle.runtime.compose) + implementation(libs.androidx.datastore.preferences) + + //Room + implementation(libs.androidx.room.runtime) + ksp(libs.androidx.room.compiler) + implementation(libs.androidx.room.ktx) + + //Json and GeoJson + implementation(libs.geojson.jackson) + implementation(libs.jackson.module.kotlin) + + implementation(libs.retrofit) + implementation(libs.converterJackson) + + //Glide + implementation(libs.compose) + testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/app/src/androidTest/java/cz/cvut/fel/pda/financemanagement/ExampleInstrumentedTest.kt b/app/src/androidTest/java/cz/cvut/fel/pda/financemanagement/ExampleInstrumentedTest.kt index b729528..cbe2fe2 100644 --- a/app/src/androidTest/java/cz/cvut/fel/pda/financemanagement/ExampleInstrumentedTest.kt +++ b/app/src/androidTest/java/cz/cvut/fel/pda/financemanagement/ExampleInstrumentedTest.kt @@ -1,13 +1,11 @@ package cz.cvut.fel.pda.financemanagement -import androidx.test.platform.app.InstrumentationRegistry import androidx.test.ext.junit.runners.AndroidJUnit4 - +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith -import org.junit.Assert.* - /** * Instrumented test, which will execute on an Android device. * diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4509b3a..024c48e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ xmlns:tools="http://schemas.android.com/tools"> <application + android:name=".FinanceManagementApplication" android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" @@ -11,11 +12,10 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.FinanceManagement" - tools:targetApi="31"> + > <activity android:name=".MainActivity" android:exported="true" - android:label="@string/app_name" android:theme="@style/Theme.FinanceManagement"> <intent-filter> <action android:name="android.intent.action.MAIN" /> diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApp.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApp.kt index 78f9a69..6760bdf 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApp.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApp.kt @@ -9,7 +9,7 @@ import androidx.navigation.compose.rememberNavController import cz.cvut.fel.pda.financemanagement.ui.screens.AccountsScreen import cz.cvut.fel.pda.financemanagement.ui.screens.CategoriesScreen import cz.cvut.fel.pda.financemanagement.ui.screens.LoansScreen -import cz.cvut.fel.pda.financemanagement.ui.screens.TransactionsScreen +import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.TransactionsScreen enum class FinanceManagementScreens() { diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApplication.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApplication.kt new file mode 100644 index 0000000..785cbdd --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApplication.kt @@ -0,0 +1,13 @@ +package cz.cvut.fel.pda.financemanagement + +import android.app.Application +import cz.cvut.fel.pda.financemanagement.di.AppContainer +import cz.cvut.fel.pda.financemanagement.di.AppDataContainer +class FinanceManagementApplication : Application(){ + lateinit var container: AppContainer + + override fun onCreate() { + super.onCreate() + container = AppDataContainer(this) + } +} \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/MainActivity.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/MainActivity.kt index ba7ceb9..35c973b 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/MainActivity.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/MainActivity.kt @@ -3,13 +3,6 @@ package cz.cvut.fel.pda.financemanagement import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview import cz.cvut.fel.pda.financemanagement.ui.theme.FinanceManagementTheme class MainActivity : ComponentActivity() { diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/OfflineTransactionRepository.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/OfflineTransactionRepository.kt new file mode 100644 index 0000000..d2dd746 --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/OfflineTransactionRepository.kt @@ -0,0 +1,12 @@ +package cz.cvut.fel.pda.financemanagement.database + +import kotlinx.coroutines.flow.Flow + +class OfflineTransactionRepository(private val transactionDao: TransactionDao): TransactionRepository{ + override fun getAllTransactionsStream(): Flow<List<Transaction>> = transactionDao.getAllTransactions() + override fun getTransactionForIdStream(id: Long): Flow<Transaction?> = transactionDao.getTransactionForId(id) + override suspend fun insertTransactions(vararg transactions: Transaction) = transactionDao.insertTransactions(*transactions) + override suspend fun deleteTransaction(transaction: Transaction) = transactionDao.delete(transaction) + override suspend fun deleteAllTransactions() = transactionDao.deleteAll() + override suspend fun updateTransactions(vararg transactions: Transaction) = transactionDao.updateTransactions(*transactions) +} \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/Transaction.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/Transaction.kt new file mode 100644 index 0000000..fe7ecd1 --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/Transaction.kt @@ -0,0 +1,15 @@ +package cz.cvut.fel.pda.financemanagement.database + +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.math.BigDecimal + + +@Entity(tableName = "transaction") +data class Transaction( + @PrimaryKey(autoGenerate = true) + val id: Long = 0, + val description: String = "", + val imageUrl: String = "", + val amount: BigDecimal = BigDecimal.ZERO +) diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/TransactionDao.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/TransactionDao.kt new file mode 100644 index 0000000..50d5aa3 --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/TransactionDao.kt @@ -0,0 +1,34 @@ +package cz.cvut.fel.pda.financemanagement.database + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.Update +import kotlinx.coroutines.flow.Flow + +@Dao +interface TransactionDao { + + @Query("SELECT * FROM `transaction` ORDER BY id ASC") + fun getAllTransactions(): Flow<List<Transaction>> + + @Query("SELECT * FROM `transaction` WHERE id = :id") + fun getTransactionForId(id: Long): Flow<Transaction> + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertTransactions(vararg transactions: Transaction) + + @Update + suspend fun updateTransactions(vararg transactions: Transaction) + + @Delete + suspend fun delete(item: Transaction) + + @Query("DELETE FROM `transaction`") + suspend fun deleteAll() + + @Query("SELECT * FROM `transaction` ORDER BY id ASC") + suspend fun getTransactions(): List<Transaction> +} \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/TransactionDatabase.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/TransactionDatabase.kt new file mode 100644 index 0000000..295d19f --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/TransactionDatabase.kt @@ -0,0 +1,34 @@ +package cz.cvut.fel.pda.financemanagement.database + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import androidx.room.TypeConverters +import cz.cvut.fel.pda.financemanagement.database.typeConvertion.Converters + +@Database(entities = [Transaction::class], version = 4, exportSchema = false) +@TypeConverters(Converters::class) +abstract class TransactionDatabase : RoomDatabase(){ + abstract fun transactionDao(): TransactionDao + + companion object{ + @Volatile + private var INSTANCE: TransactionDatabase? = null + + fun getDatabase(context: Context): TransactionDatabase { + return INSTANCE ?: synchronized(this) { + Room.databaseBuilder( + context.applicationContext, + TransactionDatabase::class.java, + "transaction_database" + ) + .fallbackToDestructiveMigration() + .build() + .also { + INSTANCE = it + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/TransactionRepository.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/TransactionRepository.kt new file mode 100644 index 0000000..d16ad62 --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/TransactionRepository.kt @@ -0,0 +1,12 @@ +package cz.cvut.fel.pda.financemanagement.database + +import kotlinx.coroutines.flow.Flow + +interface TransactionRepository { + fun getAllTransactionsStream(): Flow<List<Transaction>> + fun getTransactionForIdStream(id: Long): Flow<Transaction?> + suspend fun insertTransactions(vararg transactions: Transaction) + suspend fun deleteTransaction(transaction: Transaction) + suspend fun deleteAllTransactions() + suspend fun updateTransactions(vararg transactions: Transaction) +} \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/typeConvertion/Converters.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/typeConvertion/Converters.kt new file mode 100644 index 0000000..23841c0 --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/typeConvertion/Converters.kt @@ -0,0 +1,16 @@ +package cz.cvut.fel.pda.financemanagement.database.typeConvertion + +import androidx.room.TypeConverter +import java.math.BigDecimal + +class Converters { + @TypeConverter + fun fromBigDecimal(value: BigDecimal): String { + return value.toString() + } + + @TypeConverter + fun toBigDecimal(value: String): BigDecimal { + return BigDecimal(value) + } +} \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/di/AppContainer.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/di/AppContainer.kt new file mode 100644 index 0000000..7510af9 --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/di/AppContainer.kt @@ -0,0 +1,16 @@ +package cz.cvut.fel.pda.financemanagement.di + +import android.content.Context +import cz.cvut.fel.pda.financemanagement.database.OfflineTransactionRepository +import cz.cvut.fel.pda.financemanagement.database.TransactionDatabase +import cz.cvut.fel.pda.financemanagement.database.TransactionRepository + +interface AppContainer { + val transactionRepository: TransactionRepository +} + +class AppDataContainer(private val context: Context) : AppContainer { + override val transactionRepository: TransactionRepository by lazy { + OfflineTransactionRepository(TransactionDatabase.getDatabase(context).transactionDao()) + } +} \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/TransactionCategory.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/TransactionCategory.kt new file mode 100644 index 0000000..a357182 --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/TransactionCategory.kt @@ -0,0 +1,17 @@ +package cz.cvut.fel.pda.financemanagement.model + +enum class Category { + SALARY, + RENT, + GROCERIES, +} + +enum class Type { + INCOME, + EXPENSE +} + +data class TransactionCategory( + val type: Type = Type.EXPENSE, + val category: Category = Category.RENT +) \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/common/Result.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/common/Result.kt new file mode 100644 index 0000000..e06b3ed --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/common/Result.kt @@ -0,0 +1,16 @@ +package cz.cvut.fel.pda.financemanagement.model.common + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart + +sealed interface Result<out T> { + data class Success<T>(val data: T) : Result<T> + data class Error(val exception: Throwable) : Result<Nothing> + data object Loading : Result<Nothing> +} + +fun <T> Flow<T>.asResult(): Flow<Result<T>> = map<T, Result<T>> { Result.Success(it) } + .onStart { emit(Result.Loading) } + .catch { emit(Result.Error(it)) } diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/BottomNavigation.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/BottomNavigation.kt index 9828e37..ef012a3 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/BottomNavigation.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/BottomNavigation.kt @@ -34,7 +34,7 @@ fun BottomNavigation(navController: NavHostController) { selected = currentRoute == it.second, onClick = { navController.navigate(it.second) { - // Avoid multiple copies of the same destination when reselecting the same item + // Avoid multiple copies of the same destination when re-selecting the same item launchSingleTop = true popUpTo(navController.graph.findStartDestination().id) { saveState = true // Save the state of all destinations before popping them diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/CommonScreenForState.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/CommonScreenForState.kt new file mode 100644 index 0000000..c2d6867 --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/CommonScreenForState.kt @@ -0,0 +1,47 @@ +package cz.cvut.fel.pda.financemanagement.ui.components + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun ShowErrorScreen( + errorMessage: String = "Error", + onClick : () -> Unit = { }, + innerPaddings: PaddingValues = PaddingValues(0.dp) +){ + Surface( + modifier = Modifier + .fillMaxSize() + .padding(innerPaddings) + .clickable { onClick() } + ) { + Box( + contentAlignment = Alignment.Center + ){ + Text(text = errorMessage) + } + } +} + +@Composable +fun ShowLoadingScreen( + innerPaddings: PaddingValues = PaddingValues(0.dp) +){ + Surface( + modifier = Modifier + .fillMaxSize() + .padding(innerPaddings) + ) { + CircularProgressIndicator() + } +} diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/TopBar.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/TopBar.kt index dd3c868..3512327 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/TopBar.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/TopBar.kt @@ -8,11 +8,11 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.sp -import androidx.compose.ui.Alignment @Composable fun TopBar() { diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/TransactionsScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/TransactionsScreen.kt deleted file mode 100644 index 186368c..0000000 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/TransactionsScreen.kt +++ /dev/null @@ -1,125 +0,0 @@ -package cz.cvut.fel.pda.financemanagement.ui.screens - -import androidx.compose.foundation.border -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowDropDown -import androidx.compose.material.icons.filled.MoreVert -import androidx.compose.material.icons.outlined.Add -import androidx.compose.material3.Divider -import androidx.compose.material3.FloatingActionButton -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.navigation.NavHostController -import androidx.navigation.compose.rememberNavController -import androidx.compose.ui.graphics.Color -import cz.cvut.fel.pda.financemanagement.ui.components.BottomNavigation -import cz.cvut.fel.pda.financemanagement.ui.components.TopBar -import cz.cvut.fel.pda.financemanagement.ui.theme.FinanceManagementTheme - -@Composable -fun TransactionsScreen( - navController: NavHostController -) { - Scaffold( - topBar = { - TopBar() - }, - floatingActionButton = { - FloatingActionButton(onClick = { /*TODO*/ }) { - Icon(Icons.Outlined.Add, contentDescription = "Add transaction") - } - }, - bottomBar = { - BottomNavigation(navController = navController) - }, - ){ innerPadding -> - TransactionScreenContent(innerPadding) - } -} - -@Composable -fun TransactionScreenContent(paddingValues: PaddingValues) { - Column(modifier = Modifier - .padding(paddingValues) - .fillMaxWidth()) { - Row(modifier = Modifier - .align(Alignment.CenterHorizontally) - .padding(14.dp)) { - Text(text = "Total: ", style = TextStyle(fontSize = 24.sp, fontWeight = FontWeight.Bold)) - Text(text = "46.245 CZK", - style = TextStyle( - fontSize = 24.sp, - fontWeight = FontWeight.Bold, - color = Color(50, 168, 82) - ) - ) - } - Column(modifier = Modifier.padding(horizontal = 14.dp)) { - Row(modifier = Modifier - .fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween - ){ - Text(text = "Transactions", style = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Bold)) - Row{ - Text(text = "sort") - Icon(Icons.Filled.ArrowDropDown, contentDescription = "Sort options") - } - - } - Divider() - - TransactionItem(TransactionType.GROCERIES, 5000) - TransactionItem(TransactionType.SALARY, 47000) - } - } -} - -enum class TransactionType { - GROCERIES, SALARY -} - -@Composable -fun TransactionItem(type: TransactionType, sum: Int) { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 8.dp) - .border(1.dp, Color.Gray, shape = RoundedCornerShape(3.dp)) - .padding(8.dp) - , horizontalArrangement = Arrangement.SpaceBetween - ) { - Column { - Text(text = type.name, style = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Bold)) - Text(text = sum.toString(), style = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Bold)) - } - IconButton(onClick = { /* Handle click event here */ }) { - Icon(Icons.Filled.MoreVert, contentDescription = "Options") - } - } -} - -@Preview(showBackground = true) -@Composable -fun TransactionScreenPreview() { - FinanceManagementTheme { - TransactionsScreen(navController = rememberNavController()) - } -} \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionViewModel.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionViewModel.kt new file mode 100644 index 0000000..2d99e18 --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionViewModel.kt @@ -0,0 +1,65 @@ +package cz.cvut.fel.pda.financemanagement.ui.screens.transactions + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import cz.cvut.fel.pda.financemanagement.database.Transaction +import cz.cvut.fel.pda.financemanagement.database.TransactionRepository +import cz.cvut.fel.pda.financemanagement.model.common.Result +import cz.cvut.fel.pda.financemanagement.model.common.asResult +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch + +class TransactionViewModel( + private val transactionRepository: TransactionRepository, +) : ViewModel(){ + val transactions : StateFlow<TransactionsUiState> = + transactionRepository + .getAllTransactionsStream() + .asResult() + .map { allTransactionsToResult -> + when (allTransactionsToResult) { + is Result.Error -> TransactionsUiState.Error(Exception(allTransactionsToResult.exception)) + is Result.Loading -> TransactionsUiState.Loading + is Result.Success -> TransactionsUiState.Success(allTransactionsToResult.data) + } + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000L), + initialValue = TransactionsUiState.Loading + ) + fun addTransaction(transaction: Transaction) { + viewModelScope.launch { + transactionRepository.insertTransactions( + transaction) + } + } +/* + fun addRandomTransaction() { + viewModelScope.launch { + transactionRepository.insertPlaygrounds( + assetsGeojsonFileRepository.getRandomPlayground().toPlayground() + ) + } + } + + fun importTransactions() { + viewModelScope.launch { + transactionRepository.insertPlaygrounds(*assetsGeojsonFileRepository.getAllPlaygrounds().map { it.toPlayground() }.toTypedArray()) + } + }*/ + + fun deleteAllTransactions() { + viewModelScope.launch { + transactionRepository.deleteAllTransactions() + } + } +} + +sealed interface TransactionsUiState { + data object Loading : TransactionsUiState + data class Success(val itemList: List<Transaction> = listOf()) : TransactionsUiState + data class Error(val exception: Exception) : TransactionsUiState +} \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt new file mode 100644 index 0000000..bef3ba2 --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt @@ -0,0 +1,184 @@ +package cz.cvut.fel.pda.financemanagement.ui.screens.transactions + +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +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.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowDropDown +import androidx.compose.material.icons.filled.MoreVert +import androidx.compose.material.icons.outlined.Add +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +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.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavHostController +import androidx.navigation.compose.rememberNavController +import cz.cvut.fel.pda.financemanagement.database.Transaction +import cz.cvut.fel.pda.financemanagement.model.Type +import cz.cvut.fel.pda.financemanagement.ui.components.BottomNavigation +import cz.cvut.fel.pda.financemanagement.ui.components.ShowErrorScreen +import cz.cvut.fel.pda.financemanagement.ui.components.ShowLoadingScreen +import cz.cvut.fel.pda.financemanagement.ui.components.TopBar +import cz.cvut.fel.pda.financemanagement.ui.theme.FinanceManagementTheme +import cz.cvut.fel.pda.financemanagement.ui.utils.AppViewModelProvider +import java.math.BigDecimal + +@Composable +fun TransactionsScreen( + navController: NavHostController, + transactionViewModel: TransactionViewModel= viewModel( + factory = AppViewModelProvider.Factory + ) +) { + val transactionsUiState: TransactionsUiState by transactionViewModel.transactions.collectAsStateWithLifecycle() + Scaffold( + topBar = { TopBar() }, + content = { innerPadding -> + when (transactionsUiState) { + is TransactionsUiState.Loading -> { + ShowLoadingScreen() + } + is TransactionsUiState.Error -> { + ShowErrorScreen( + errorMessage = (transactionsUiState as TransactionsUiState.Error).exception.message ?: "Error", + ) + } + is TransactionsUiState.Success -> { + TransactionScreenContent( (transactionsUiState as TransactionsUiState.Success).itemList, innerPadding) + } + } + }, + floatingActionButton = { + FloatingActionButton(onClick = { /*TODO*/ }) { + Icon(Icons.Outlined.Add, contentDescription = "Add transaction") + } + }, + bottomBar = { BottomNavigation(navController = navController) }, + ) +} + +@Composable +fun TransactionScreenContent(transactions: List<Transaction>, innerPadding: PaddingValues) { + Column(modifier = Modifier + .padding(innerPadding) + .fillMaxWidth()) { + TotalSumOfTransactions(transactions) + Column(modifier = Modifier.padding(horizontal = 14.dp)) { + Row(modifier = Modifier + .fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ){ + Text(text = "Transactions", style = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Bold)) + Row{ + Text(text = "sort") + Icon(Icons.Filled.ArrowDropDown, contentDescription = "Sort options") + } + + } + HorizontalDivider() + ShowTabListSuccessScreen(transactions) + } + } +} + +@Composable +fun TotalSumOfTransactions(transactions: List<Transaction>) { + val totalSum = transactions.sumOf { it.amount }.toString() + Row(modifier = Modifier.fillMaxWidth().padding(14.dp), + horizontalArrangement = Arrangement.Center){ + Text(text = "Total: ", style = TextStyle(fontSize = 24.sp, fontWeight = FontWeight.Bold)) + Text(text = "$totalSum CZK", + style = TextStyle( + fontSize = 24.sp, + fontWeight = FontWeight.Bold, + color = Color(50, 168, 82) + ) + ) + } +} + +@Composable +fun ShowTabListSuccessScreen(itemList: List<Transaction>) { + if (itemList.isEmpty()){ + Box ( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + Text( + text = "No transactions found.", + textAlign = TextAlign.Center, + style = MaterialTheme.typography.titleLarge, + modifier = Modifier.padding(16.dp) + ) + } + } else { + TransactionsList(itemList) + } +} + +@Composable +fun TransactionsList( + itemList: List<Transaction>, + modifier: Modifier = Modifier +) { + LazyColumn( + modifier = modifier.fillMaxSize(), + verticalArrangement = Arrangement.Top + ) { + items(itemList) { _ -> + TransactionItem(Type.EXPENSE, 10.toBigDecimal()) + } + } +} +@Composable +fun TransactionItem(type: Type, sum: BigDecimal) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp) + .border(1.dp, Color.Gray, shape = RoundedCornerShape(3.dp)) + .padding(8.dp) + , horizontalArrangement = Arrangement.SpaceBetween + ) { + Column { + Text(text = type.name, style = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Bold)) + Text(text = sum.toString(), style = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Bold)) + } + IconButton(onClick = { /* Handle click event here */ }) { + Icon(Icons.Filled.MoreVert, contentDescription = "Options") + } + } +} + +@Preview(showBackground = true) +@Composable +fun TransactionScreenPreview() { + FinanceManagementTheme { + TransactionsScreen(navController = rememberNavController()) + } +} \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/utils/AppViewModelProvider.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/utils/AppViewModelProvider.kt new file mode 100644 index 0000000..a9604da --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/utils/AppViewModelProvider.kt @@ -0,0 +1,21 @@ +package cz.cvut.fel.pda.financemanagement.ui.utils + +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewmodel.CreationExtras +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory +import cz.cvut.fel.pda.financemanagement.FinanceManagementApplication +import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.TransactionViewModel + +object AppViewModelProvider { + val Factory = viewModelFactory { + initializer { + TransactionViewModel( + financemanagementApplication().container.transactionRepository + ) + } + } +} + +fun CreationExtras.financemanagementApplication(): FinanceManagementApplication = + (this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as FinanceManagementApplication) \ No newline at end of file diff --git a/app/src/test/java/cz/cvut/fel/pda/financemanagement/ExampleUnitTest.kt b/app/src/test/java/cz/cvut/fel/pda/financemanagement/ExampleUnitTest.kt index 373bbb0..dedf2d5 100644 --- a/app/src/test/java/cz/cvut/fel/pda/financemanagement/ExampleUnitTest.kt +++ b/app/src/test/java/cz/cvut/fel/pda/financemanagement/ExampleUnitTest.kt @@ -1,9 +1,8 @@ package cz.cvut.fel.pda.financemanagement +import org.junit.Assert.assertEquals import org.junit.Test -import org.junit.Assert.* - /** * Example local unit test, which will execute on the development machine (host). * diff --git a/build.gradle.kts b/build.gradle.kts index a0985ef..0374c1c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,4 +2,5 @@ plugins { alias(libs.plugins.androidApplication) apply false alias(libs.plugins.jetbrainsKotlinAndroid) apply false + alias(libs.plugins.ksp) apply false } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ae97388..024cf92 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,18 +1,34 @@ [versions] -agp = "8.3.0" +agp = "8.3.2" +glideCompose = "1.0.0-beta01" +datastorePreferences = "1.0.0" +jacksonModuleKotlin = "2.12.0" +geojsonJackson = "1.6" kotlin = "1.9.0" +ksp = "1.9.0-1.0.13" coreKtx = "1.12.0" junit = "4.13.2" junitVersion = "1.1.5" espressoCore = "3.5.1" lifecycleRuntimeKtx = "2.7.0" activityCompose = "1.8.2" -composeBom = "2023.08.00" -navigationRuntimeKtx = "2.7.7" +composeBom = "2024.04.00" navigationCompose = "2.7.7" +lifecycleRuntimeCompose = "2.7.0" +roomVersion = "2.6.1" +retrofit = "2.9.0" [libraries] +retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" } +converterJackson = { module = "com.squareup.retrofit2:converter-jackson", version.ref = "retrofit" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } +androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" } +androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomVersion" } +androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "roomVersion" } +androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomVersion" } +compose = { module = "com.github.bumptech.glide:compose", version.ref = "glideCompose" } +jackson-module-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jacksonModuleKotlin" } +geojson-jackson = { module = "de.grundid.opendatalab:geojson-jackson", version.ref = "geojsonJackson" } junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } @@ -26,10 +42,11 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-material3 = { group = "androidx.compose.material3", name = "material3" } -androidx-navigation-runtime-ktx = { group = "androidx.navigation", name = "navigation-runtime-ktx", version.ref = "navigationRuntimeKtx" } androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" } +androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycleRuntimeCompose" } [plugins] androidApplication = { id = "com.android.application", version.ref = "agp" } jetbrainsKotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } -- GitLab From 8430594b41a8ea3e9a83172502e952805f068d9a Mon Sep 17 00:00:00 2001 From: dovicrad <dovicrad@fel.cvut.cz> Date: Thu, 18 Apr 2024 10:59:46 +0200 Subject: [PATCH 02/12] :cooking: semi working transaction creation , saving and display --- .../financemanagement/FinanceManagementApp.kt | 10 ++ .../OfflineTransactionRepository.kt | 5 +- .../database/{ => transaction}/Transaction.kt | 2 +- .../{ => transaction}/TransactionDao.kt | 2 +- .../{ => transaction}/TransactionDatabase.kt | 2 +- .../TransactionRepository.kt | 2 +- .../pda/financemanagement/di/AppContainer.kt | 6 +- .../transactions/EditTransactionScreen.kt | 133 ++++++++++++++++++ .../TransactionDetailViewModel.kt | 55 ++++++++ .../transactions/TransactionViewModel.kt | 4 +- .../transactions/TransactionsScreen.kt | 15 +- .../ui/utils/AppViewModelProvider.kt | 8 ++ 12 files changed, 226 insertions(+), 18 deletions(-) rename app/src/main/java/cz/cvut/fel/pda/financemanagement/database/{ => transaction}/OfflineTransactionRepository.kt (87%) rename app/src/main/java/cz/cvut/fel/pda/financemanagement/database/{ => transaction}/Transaction.kt (83%) rename app/src/main/java/cz/cvut/fel/pda/financemanagement/database/{ => transaction}/TransactionDao.kt (93%) rename app/src/main/java/cz/cvut/fel/pda/financemanagement/database/{ => transaction}/TransactionDatabase.kt (94%) rename app/src/main/java/cz/cvut/fel/pda/financemanagement/database/{ => transaction}/TransactionRepository.kt (87%) create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/EditTransactionScreen.kt create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailViewModel.kt diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApp.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApp.kt index 6760bdf..abf31dd 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApp.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApp.kt @@ -3,17 +3,21 @@ package cz.cvut.fel.pda.financemanagement import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.navigation.NavHostController +import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController +import androidx.navigation.navArgument import cz.cvut.fel.pda.financemanagement.ui.screens.AccountsScreen import cz.cvut.fel.pda.financemanagement.ui.screens.CategoriesScreen import cz.cvut.fel.pda.financemanagement.ui.screens.LoansScreen +import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.EditTransactionScreen import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.TransactionsScreen enum class FinanceManagementScreens() { Transactions, + EditTransaction, Accounts, Categories, Loans @@ -41,5 +45,11 @@ fun FinanceManagementApp() { composable(route = FinanceManagementScreens.Categories.name) { CategoriesScreen(navController) } + composable(route = "${FinanceManagementScreens.EditTransaction.name}/{Id}", + arguments = listOf(navArgument("Id") { type = NavType.LongType })){ + EditTransactionScreen(navController, + transitionId = it.arguments?.getLong("Id") + ) + } } } \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/OfflineTransactionRepository.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/OfflineTransactionRepository.kt similarity index 87% rename from app/src/main/java/cz/cvut/fel/pda/financemanagement/database/OfflineTransactionRepository.kt rename to app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/OfflineTransactionRepository.kt index d2dd746..6015f19 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/OfflineTransactionRepository.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/OfflineTransactionRepository.kt @@ -1,8 +1,9 @@ -package cz.cvut.fel.pda.financemanagement.database +package cz.cvut.fel.pda.financemanagement.database.transaction import kotlinx.coroutines.flow.Flow -class OfflineTransactionRepository(private val transactionDao: TransactionDao): TransactionRepository{ +class OfflineTransactionRepository(private val transactionDao: TransactionDao): + TransactionRepository { override fun getAllTransactionsStream(): Flow<List<Transaction>> = transactionDao.getAllTransactions() override fun getTransactionForIdStream(id: Long): Flow<Transaction?> = transactionDao.getTransactionForId(id) override suspend fun insertTransactions(vararg transactions: Transaction) = transactionDao.insertTransactions(*transactions) diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/Transaction.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/Transaction.kt similarity index 83% rename from app/src/main/java/cz/cvut/fel/pda/financemanagement/database/Transaction.kt rename to app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/Transaction.kt index fe7ecd1..540a53c 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/Transaction.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/Transaction.kt @@ -1,4 +1,4 @@ -package cz.cvut.fel.pda.financemanagement.database +package cz.cvut.fel.pda.financemanagement.database.transaction import androidx.room.Entity import androidx.room.PrimaryKey diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/TransactionDao.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDao.kt similarity index 93% rename from app/src/main/java/cz/cvut/fel/pda/financemanagement/database/TransactionDao.kt rename to app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDao.kt index 50d5aa3..e31cc3c 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/TransactionDao.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDao.kt @@ -1,4 +1,4 @@ -package cz.cvut.fel.pda.financemanagement.database +package cz.cvut.fel.pda.financemanagement.database.transaction import androidx.room.Dao import androidx.room.Delete diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/TransactionDatabase.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDatabase.kt similarity index 94% rename from app/src/main/java/cz/cvut/fel/pda/financemanagement/database/TransactionDatabase.kt rename to app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDatabase.kt index 295d19f..885e02a 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/TransactionDatabase.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDatabase.kt @@ -1,4 +1,4 @@ -package cz.cvut.fel.pda.financemanagement.database +package cz.cvut.fel.pda.financemanagement.database.transaction import android.content.Context import androidx.room.Database diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/TransactionRepository.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionRepository.kt similarity index 87% rename from app/src/main/java/cz/cvut/fel/pda/financemanagement/database/TransactionRepository.kt rename to app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionRepository.kt index d16ad62..64fef70 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/TransactionRepository.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionRepository.kt @@ -1,4 +1,4 @@ -package cz.cvut.fel.pda.financemanagement.database +package cz.cvut.fel.pda.financemanagement.database.transaction import kotlinx.coroutines.flow.Flow diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/di/AppContainer.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/di/AppContainer.kt index 7510af9..51b346a 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/di/AppContainer.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/di/AppContainer.kt @@ -1,9 +1,9 @@ package cz.cvut.fel.pda.financemanagement.di import android.content.Context -import cz.cvut.fel.pda.financemanagement.database.OfflineTransactionRepository -import cz.cvut.fel.pda.financemanagement.database.TransactionDatabase -import cz.cvut.fel.pda.financemanagement.database.TransactionRepository +import cz.cvut.fel.pda.financemanagement.database.transaction.OfflineTransactionRepository +import cz.cvut.fel.pda.financemanagement.database.transaction.TransactionDatabase +import cz.cvut.fel.pda.financemanagement.database.transaction.TransactionRepository interface AppContainer { val transactionRepository: TransactionRepository diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/EditTransactionScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/EditTransactionScreen.kt new file mode 100644 index 0000000..abc86f3 --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/EditTransactionScreen.kt @@ -0,0 +1,133 @@ +package cz.cvut.fel.pda.financemanagement.ui.screens.transactions + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Close +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavHostController +import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction +import cz.cvut.fel.pda.financemanagement.ui.components.ShowErrorScreen +import cz.cvut.fel.pda.financemanagement.ui.components.ShowLoadingScreen +import cz.cvut.fel.pda.financemanagement.ui.utils.AppViewModelProvider +import java.math.BigDecimal + +@Composable +fun EditTransactionScreen(navController: NavHostController, + transitionId: Long?, + transitionDetailViewModel: TransactionDetailViewModel = viewModel( + factory = AppViewModelProvider.Factory +),){ + val uiState by transitionDetailViewModel.uiState.collectAsStateWithLifecycle() + + when (val state = uiState) { + is TransactionUiState.Error -> { + ShowErrorScreen( + errorMessage = state.exception.message ?: "Error", + onClick = { navController.popBackStack() } + ) + } + TransactionUiState.Loading -> { + ShowLoadingScreen() + } + is TransactionUiState.Success -> { + ShowSuccessScreen( + state.transaction, + navController, + transitionDetailViewModel + ) + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ShowSuccessScreen( + transaction: Transaction, + navController: NavHostController, + transactionViewModel: TransactionDetailViewModel +) { + val amount = rememberSaveable { mutableStateOf(transaction.amount.toString()) } + + Scaffold( + topBar = { + TopAppBar( + title = { Text("neco") }, + colors = TopAppBarDefaults.mediumTopAppBarColors( + containerColor = MaterialTheme.colorScheme.primaryContainer + ), + modifier = Modifier, + navigationIcon = { + IconButton(onClick = { navController.popBackStack() }) { + Icon( + imageVector = Icons.Outlined.Close, + contentDescription = "cs" + ) + } + }, + actions = { + TextButton( + onClick = { + transactionViewModel.savePlayground( + amount = BigDecimal(amount.value) + ) + navController.popBackStack() + }, + modifier = Modifier.padding(16.dp) + ) { + Text(text = "save") + } + } + ) + }, + content = {innerPadding -> + EditTransactionDetailSuccessContent(innerPadding, amount) + } + ) +} +@Composable +fun EditTransactionDetailSuccessContent(innerPadding: PaddingValues, amount: MutableState<String>) { + val openDialog = remember { mutableStateOf(false) } + + Column ( + modifier = Modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()) + .padding(innerPadding), // use innerPadding here + horizontalAlignment = Alignment.CenterHorizontally + ) { + val spaceModifier = Modifier + .fillMaxWidth() + .padding(24.dp, 4.dp) + OutlinedTextField( + value = amount.value, + onValueChange = { newValue -> amount.value = newValue }, + label = { Text("xd") }, + modifier = spaceModifier + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailViewModel.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailViewModel.kt new file mode 100644 index 0000000..753dfe3 --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailViewModel.kt @@ -0,0 +1,55 @@ +package cz.cvut.fel.pda.financemanagement.ui.screens.transactions + +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction +import cz.cvut.fel.pda.financemanagement.database.transaction.TransactionRepository +import kotlinx.coroutines.flow.StateFlow +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch +import java.math.BigDecimal + +class TransactionDetailViewModel( + private val transactionRepository: TransactionRepository, + savedStateHandle: SavedStateHandle +): ViewModel() { + private val transactionId: Long = savedStateHandle.get<Long>("Id")!! + + val uiState: StateFlow<TransactionUiState> = + transactionRepository + .getTransactionForIdStream(transactionId) + .map { + if (it != null) { + TransactionUiState.Success(it) + } else if (transactionId == 0L) { + TransactionUiState.Success(Transaction()) + } else { + TransactionUiState.Error(Exception("Transaction not found")) + } + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000L), + initialValue = TransactionUiState.Loading + ) + fun savePlayground( + amount: BigDecimal + ) { + viewModelScope.launch { + transactionRepository.insertTransactions( + Transaction( + id = transactionId, + amount = amount + ) + ) + } + } +} + +sealed interface TransactionUiState { + data object Loading : TransactionUiState + data class Success(val transaction: Transaction) : TransactionUiState + data class Error(val exception: Exception) : TransactionUiState +} \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionViewModel.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionViewModel.kt index 2d99e18..ecae25f 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionViewModel.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionViewModel.kt @@ -2,8 +2,8 @@ package cz.cvut.fel.pda.financemanagement.ui.screens.transactions import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import cz.cvut.fel.pda.financemanagement.database.Transaction -import cz.cvut.fel.pda.financemanagement.database.TransactionRepository +import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction +import cz.cvut.fel.pda.financemanagement.database.transaction.TransactionRepository import cz.cvut.fel.pda.financemanagement.model.common.Result import cz.cvut.fel.pda.financemanagement.model.common.asResult import kotlinx.coroutines.flow.SharingStarted diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt index bef3ba2..991daa4 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt @@ -38,7 +38,8 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController import androidx.navigation.compose.rememberNavController -import cz.cvut.fel.pda.financemanagement.database.Transaction +import cz.cvut.fel.pda.financemanagement.FinanceManagementScreens +import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction import cz.cvut.fel.pda.financemanagement.model.Type import cz.cvut.fel.pda.financemanagement.ui.components.BottomNavigation import cz.cvut.fel.pda.financemanagement.ui.components.ShowErrorScreen @@ -74,7 +75,7 @@ fun TransactionsScreen( } }, floatingActionButton = { - FloatingActionButton(onClick = { /*TODO*/ }) { + FloatingActionButton(onClick = { navController.navigate("${FinanceManagementScreens.EditTransaction.name}/0")}) { Icon(Icons.Outlined.Add, contentDescription = "Add transaction") } }, @@ -150,13 +151,13 @@ fun TransactionsList( modifier = modifier.fillMaxSize(), verticalArrangement = Arrangement.Top ) { - items(itemList) { _ -> - TransactionItem(Type.EXPENSE, 10.toBigDecimal()) + items(itemList) { transaction-> + TransactionItem(transaction) } } } @Composable -fun TransactionItem(type: Type, sum: BigDecimal) { +fun TransactionItem(transaction: Transaction) { Row( modifier = Modifier .fillMaxWidth() @@ -166,8 +167,8 @@ fun TransactionItem(type: Type, sum: BigDecimal) { , horizontalArrangement = Arrangement.SpaceBetween ) { Column { - Text(text = type.name, style = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Bold)) - Text(text = sum.toString(), style = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Bold)) + Text(text = transaction.description, style = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Bold)) + Text(text = transaction.amount.toString(), style = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Bold)) } IconButton(onClick = { /* Handle click event here */ }) { Icon(Icons.Filled.MoreVert, contentDescription = "Options") diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/utils/AppViewModelProvider.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/utils/AppViewModelProvider.kt index a9604da..696af66 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/utils/AppViewModelProvider.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/utils/AppViewModelProvider.kt @@ -1,10 +1,12 @@ package cz.cvut.fel.pda.financemanagement.ui.utils import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.createSavedStateHandle import androidx.lifecycle.viewmodel.CreationExtras import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory import cz.cvut.fel.pda.financemanagement.FinanceManagementApplication +import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.TransactionDetailViewModel import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.TransactionViewModel object AppViewModelProvider { @@ -14,6 +16,12 @@ object AppViewModelProvider { financemanagementApplication().container.transactionRepository ) } + initializer { + TransactionDetailViewModel( + financemanagementApplication().container.transactionRepository, + this.createSavedStateHandle() + ) + } } } -- GitLab From 040b38bf5dae2f0b196ba24f8404d861355e776f Mon Sep 17 00:00:00 2001 From: dovicrad <dovicrad@fel.cvut.cz> Date: Mon, 22 Apr 2024 16:58:10 +0200 Subject: [PATCH 03/12] :cooking: add working transaction with category relation --- app/src/main/AndroidManifest.xml | 3 +- .../financemanagement/database/AppDatabase.kt | 39 ++++++++++++++ .../database/category/Category.kt | 21 ++++++++ .../database/category/CategoryDao.kt | 34 ++++++++++++ .../database/category/CategoryRepository.kt | 12 +++++ .../OfflineTransactionRepository.kt | 3 ++ .../database/transaction/Transaction.kt | 3 +- .../database/transaction/TransactionDao.kt | 11 ++-- .../transaction/TransactionDatabase.kt | 34 ------------ .../transaction/TransactionRepository.kt | 2 + .../pda/financemanagement/di/AppContainer.kt | 4 +- .../model/CategoryWithTransaction.kt | 15 ++++++ .../model/TransactionCategory.kt | 17 ------ .../transactions/EditTransactionScreen.kt | 6 +-- .../TransactionDetailViewModel.kt | 11 ++-- .../transactions/TransactionViewModel.kt | 49 ++++------------- .../transactions/TransactionsScreen.kt | 52 +++++++++++-------- .../financemanagement/ui/utils/ColorUtils.kt | 25 +++++++++ 18 files changed, 215 insertions(+), 126 deletions(-) create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/database/AppDatabase.kt create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/database/category/Category.kt create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/database/category/CategoryDao.kt create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/database/category/CategoryRepository.kt delete mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDatabase.kt create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/model/CategoryWithTransaction.kt delete mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/model/TransactionCategory.kt create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/utils/ColorUtils.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 024c48e..c49185b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application android:name=".FinanceManagementApplication" diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/AppDatabase.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/AppDatabase.kt new file mode 100644 index 0000000..7abdd45 --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/AppDatabase.kt @@ -0,0 +1,39 @@ +package cz.cvut.fel.pda.financemanagement.database + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import androidx.room.TypeConverters +import cz.cvut.fel.pda.financemanagement.database.category.Category +import cz.cvut.fel.pda.financemanagement.database.category.CategoryDao +import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction +import cz.cvut.fel.pda.financemanagement.database.transaction.TransactionDao +import cz.cvut.fel.pda.financemanagement.database.typeConvertion.Converters + +@Database(entities = [Transaction::class, Category::class], version = 6, exportSchema = false) +@TypeConverters(Converters::class) +abstract class AppDatabase : RoomDatabase(){ + abstract fun transactionDao(): TransactionDao + abstract fun categoryDao(): CategoryDao + + companion object{ + @Volatile + private var INSTANCE: AppDatabase? = null + + fun getDatabase(context: Context): AppDatabase { + return INSTANCE ?: synchronized(this) { + Room.databaseBuilder( + context.applicationContext, + AppDatabase::class.java, + "database" + ) + .fallbackToDestructiveMigration() + .build() + .also { + INSTANCE = it + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/category/Category.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/category/Category.kt new file mode 100644 index 0000000..9cc2b60 --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/category/Category.kt @@ -0,0 +1,21 @@ +package cz.cvut.fel.pda.financemanagement.database.category + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "category") +data class Category( + @PrimaryKey(autoGenerate = true) + val id: Long = 0, + val name: CategoryName, + val type: CategoryType, +) + +enum class CategoryType { + EXPENSE, + INCOME, +} + +enum class CategoryName { + GROCERIES, +} diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/category/CategoryDao.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/category/CategoryDao.kt new file mode 100644 index 0000000..02a9323 --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/category/CategoryDao.kt @@ -0,0 +1,34 @@ +package cz.cvut.fel.pda.financemanagement.database.category + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.Update +import kotlinx.coroutines.flow.Flow + +@Dao +interface CategoryDao { + + @Query("SELECT * FROM `category` ORDER BY id ASC") + fun getAllCategories(): Flow<List<Category>> + + @Query("SELECT * FROM `category` WHERE id = :id") + fun getCategoryForId(id: Long): Flow<Category> + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertCategories(vararg transactions: Category) + + @Update + suspend fun updateCategories(vararg transactions: Category) + + @Delete + suspend fun delete(item: Category) + + @Query("DELETE FROM `category`") + suspend fun deleteAll() + + @Query("SELECT * FROM `category` ORDER BY id ASC") + suspend fun getCategories(): List<Category> +} \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/category/CategoryRepository.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/category/CategoryRepository.kt new file mode 100644 index 0000000..cfd96e9 --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/category/CategoryRepository.kt @@ -0,0 +1,12 @@ +package cz.cvut.fel.pda.financemanagement.database.category + +import kotlinx.coroutines.flow.Flow + +interface CategoryRepository { + fun getAllCategoriesStream(): Flow<List<Category>> + fun getCategoryForIdStream(id: Long): Flow<Category?> + suspend fun insertCategories(vararg categories: Category) + suspend fun deleteCategory(category: Category) + suspend fun deleteAllCategories() + suspend fun updateCategory(vararg categories: Category) +} \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/OfflineTransactionRepository.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/OfflineTransactionRepository.kt index 6015f19..f5204f7 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/OfflineTransactionRepository.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/OfflineTransactionRepository.kt @@ -1,11 +1,14 @@ package cz.cvut.fel.pda.financemanagement.database.transaction +import cz.cvut.fel.pda.financemanagement.model.CategoryWithTransaction import kotlinx.coroutines.flow.Flow class OfflineTransactionRepository(private val transactionDao: TransactionDao): TransactionRepository { override fun getAllTransactionsStream(): Flow<List<Transaction>> = transactionDao.getAllTransactions() override fun getTransactionForIdStream(id: Long): Flow<Transaction?> = transactionDao.getTransactionForId(id) + override fun getTransactionsWithCategory(): Flow<List<CategoryWithTransaction>> = transactionDao.getTransactionsWithCategory() + override suspend fun insertTransactions(vararg transactions: Transaction) = transactionDao.insertTransactions(*transactions) override suspend fun deleteTransaction(transaction: Transaction) = transactionDao.delete(transaction) override suspend fun deleteAllTransactions() = transactionDao.deleteAll() diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/Transaction.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/Transaction.kt index 540a53c..5486dd5 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/Transaction.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/Transaction.kt @@ -11,5 +11,6 @@ data class Transaction( val id: Long = 0, val description: String = "", val imageUrl: String = "", - val amount: BigDecimal = BigDecimal.ZERO + val amount: BigDecimal = BigDecimal.ZERO, + val categoryId: Long, ) diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDao.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDao.kt index e31cc3c..a47fda2 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDao.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDao.kt @@ -6,6 +6,7 @@ import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Update +import cz.cvut.fel.pda.financemanagement.model.CategoryWithTransaction import kotlinx.coroutines.flow.Flow @Dao @@ -17,6 +18,13 @@ interface TransactionDao { @Query("SELECT * FROM `transaction` WHERE id = :id") fun getTransactionForId(id: Long): Flow<Transaction> + @Query(""" + SELECT * FROM `transaction` + INNER JOIN `category` ON `transaction`.categoryId = `category`.id + ORDER BY `transaction`.id ASC +""") + fun getTransactionsWithCategory(): Flow<List<CategoryWithTransaction>> + @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertTransactions(vararg transactions: Transaction) @@ -28,7 +36,4 @@ interface TransactionDao { @Query("DELETE FROM `transaction`") suspend fun deleteAll() - - @Query("SELECT * FROM `transaction` ORDER BY id ASC") - suspend fun getTransactions(): List<Transaction> } \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDatabase.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDatabase.kt deleted file mode 100644 index 885e02a..0000000 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDatabase.kt +++ /dev/null @@ -1,34 +0,0 @@ -package cz.cvut.fel.pda.financemanagement.database.transaction - -import android.content.Context -import androidx.room.Database -import androidx.room.Room -import androidx.room.RoomDatabase -import androidx.room.TypeConverters -import cz.cvut.fel.pda.financemanagement.database.typeConvertion.Converters - -@Database(entities = [Transaction::class], version = 4, exportSchema = false) -@TypeConverters(Converters::class) -abstract class TransactionDatabase : RoomDatabase(){ - abstract fun transactionDao(): TransactionDao - - companion object{ - @Volatile - private var INSTANCE: TransactionDatabase? = null - - fun getDatabase(context: Context): TransactionDatabase { - return INSTANCE ?: synchronized(this) { - Room.databaseBuilder( - context.applicationContext, - TransactionDatabase::class.java, - "transaction_database" - ) - .fallbackToDestructiveMigration() - .build() - .also { - INSTANCE = it - } - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionRepository.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionRepository.kt index 64fef70..86f9951 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionRepository.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionRepository.kt @@ -1,10 +1,12 @@ package cz.cvut.fel.pda.financemanagement.database.transaction +import cz.cvut.fel.pda.financemanagement.model.CategoryWithTransaction import kotlinx.coroutines.flow.Flow interface TransactionRepository { fun getAllTransactionsStream(): Flow<List<Transaction>> fun getTransactionForIdStream(id: Long): Flow<Transaction?> + fun getTransactionsWithCategory(): Flow<List<CategoryWithTransaction>> suspend fun insertTransactions(vararg transactions: Transaction) suspend fun deleteTransaction(transaction: Transaction) suspend fun deleteAllTransactions() diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/di/AppContainer.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/di/AppContainer.kt index 51b346a..fdc8ec7 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/di/AppContainer.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/di/AppContainer.kt @@ -1,8 +1,8 @@ package cz.cvut.fel.pda.financemanagement.di import android.content.Context +import cz.cvut.fel.pda.financemanagement.database.AppDatabase import cz.cvut.fel.pda.financemanagement.database.transaction.OfflineTransactionRepository -import cz.cvut.fel.pda.financemanagement.database.transaction.TransactionDatabase import cz.cvut.fel.pda.financemanagement.database.transaction.TransactionRepository interface AppContainer { @@ -11,6 +11,6 @@ interface AppContainer { class AppDataContainer(private val context: Context) : AppContainer { override val transactionRepository: TransactionRepository by lazy { - OfflineTransactionRepository(TransactionDatabase.getDatabase(context).transactionDao()) + OfflineTransactionRepository(AppDatabase.getDatabase(context).transactionDao()) } } \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/CategoryWithTransaction.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/CategoryWithTransaction.kt new file mode 100644 index 0000000..7c31e6f --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/CategoryWithTransaction.kt @@ -0,0 +1,15 @@ +package cz.cvut.fel.pda.financemanagement.model + +import androidx.room.Embedded +import androidx.room.Relation +import cz.cvut.fel.pda.financemanagement.database.category.Category +import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction + +data class CategoryWithTransaction( + @Embedded val transaction: Transaction, + @Relation( + parentColumn = "categoryId", + entityColumn = "id" + ) + val category: Category +) \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/TransactionCategory.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/TransactionCategory.kt deleted file mode 100644 index a357182..0000000 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/TransactionCategory.kt +++ /dev/null @@ -1,17 +0,0 @@ -package cz.cvut.fel.pda.financemanagement.model - -enum class Category { - SALARY, - RENT, - GROCERIES, -} - -enum class Type { - INCOME, - EXPENSE -} - -data class TransactionCategory( - val type: Type = Type.EXPENSE, - val category: Category = Category.RENT -) \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/EditTransactionScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/EditTransactionScreen.kt index abc86f3..463bd58 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/EditTransactionScreen.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/EditTransactionScreen.kt @@ -19,8 +19,8 @@ import androidx.compose.material3.TextButton import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable @@ -92,8 +92,8 @@ fun ShowSuccessScreen( actions = { TextButton( onClick = { - transactionViewModel.savePlayground( - amount = BigDecimal(amount.value) + transactionViewModel.saveTransaction( + amount = BigDecimal(amount.value), ) navController.popBackStack() }, diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailViewModel.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailViewModel.kt index 753dfe3..1d34f03 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailViewModel.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailViewModel.kt @@ -2,11 +2,11 @@ package cz.cvut.fel.pda.financemanagement.ui.screens.transactions import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction import cz.cvut.fel.pda.financemanagement.database.transaction.TransactionRepository -import kotlinx.coroutines.flow.StateFlow -import androidx.lifecycle.viewModelScope import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @@ -25,7 +25,7 @@ class TransactionDetailViewModel( if (it != null) { TransactionUiState.Success(it) } else if (transactionId == 0L) { - TransactionUiState.Success(Transaction()) + TransactionUiState.Success(Transaction(categoryId = 0L)) } else { TransactionUiState.Error(Exception("Transaction not found")) } @@ -34,14 +34,15 @@ class TransactionDetailViewModel( started = SharingStarted.WhileSubscribed(5000L), initialValue = TransactionUiState.Loading ) - fun savePlayground( + fun saveTransaction( amount: BigDecimal ) { viewModelScope.launch { transactionRepository.insertTransactions( Transaction( id = transactionId, - amount = amount + amount = amount, + categoryId = 1L ) ) } diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionViewModel.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionViewModel.kt index ecae25f..f41519f 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionViewModel.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionViewModel.kt @@ -2,64 +2,37 @@ package cz.cvut.fel.pda.financemanagement.ui.screens.transactions import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction import cz.cvut.fel.pda.financemanagement.database.transaction.TransactionRepository +import cz.cvut.fel.pda.financemanagement.model.CategoryWithTransaction import cz.cvut.fel.pda.financemanagement.model.common.Result import cz.cvut.fel.pda.financemanagement.model.common.asResult import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch class TransactionViewModel( private val transactionRepository: TransactionRepository, ) : ViewModel(){ - val transactions : StateFlow<TransactionsUiState> = + val transactionsWithCategory : StateFlow<TransactionsWithCategoryUiState> = transactionRepository - .getAllTransactionsStream() + .getTransactionsWithCategory() .asResult() .map { allTransactionsToResult -> when (allTransactionsToResult) { - is Result.Error -> TransactionsUiState.Error(Exception(allTransactionsToResult.exception)) - is Result.Loading -> TransactionsUiState.Loading - is Result.Success -> TransactionsUiState.Success(allTransactionsToResult.data) + is Result.Error -> TransactionsWithCategoryUiState.Error(Exception(allTransactionsToResult.exception)) + is Result.Loading -> TransactionsWithCategoryUiState.Loading + is Result.Success -> TransactionsWithCategoryUiState.Success(allTransactionsToResult.data) } }.stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5000L), - initialValue = TransactionsUiState.Loading + initialValue = TransactionsWithCategoryUiState.Loading ) - fun addTransaction(transaction: Transaction) { - viewModelScope.launch { - transactionRepository.insertTransactions( - transaction) - } - } -/* - fun addRandomTransaction() { - viewModelScope.launch { - transactionRepository.insertPlaygrounds( - assetsGeojsonFileRepository.getRandomPlayground().toPlayground() - ) - } - } - - fun importTransactions() { - viewModelScope.launch { - transactionRepository.insertPlaygrounds(*assetsGeojsonFileRepository.getAllPlaygrounds().map { it.toPlayground() }.toTypedArray()) - } - }*/ - - fun deleteAllTransactions() { - viewModelScope.launch { - transactionRepository.deleteAllTransactions() - } - } } -sealed interface TransactionsUiState { - data object Loading : TransactionsUiState - data class Success(val itemList: List<Transaction> = listOf()) : TransactionsUiState - data class Error(val exception: Exception) : TransactionsUiState +sealed interface TransactionsWithCategoryUiState { + data object Loading : TransactionsWithCategoryUiState + data class Success(val itemList: List<CategoryWithTransaction> = listOf()) : TransactionsWithCategoryUiState + data class Error(val exception: Exception) : TransactionsWithCategoryUiState } \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt index 991daa4..925275d 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt @@ -39,15 +39,18 @@ import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController import androidx.navigation.compose.rememberNavController import cz.cvut.fel.pda.financemanagement.FinanceManagementScreens +import cz.cvut.fel.pda.financemanagement.database.category.Category +import cz.cvut.fel.pda.financemanagement.database.category.CategoryType import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction -import cz.cvut.fel.pda.financemanagement.model.Type +import cz.cvut.fel.pda.financemanagement.model.CategoryWithTransaction import cz.cvut.fel.pda.financemanagement.ui.components.BottomNavigation import cz.cvut.fel.pda.financemanagement.ui.components.ShowErrorScreen import cz.cvut.fel.pda.financemanagement.ui.components.ShowLoadingScreen import cz.cvut.fel.pda.financemanagement.ui.components.TopBar import cz.cvut.fel.pda.financemanagement.ui.theme.FinanceManagementTheme import cz.cvut.fel.pda.financemanagement.ui.utils.AppViewModelProvider -import java.math.BigDecimal +import cz.cvut.fel.pda.financemanagement.ui.utils.ColorUtils.getCategoryColor +import cz.cvut.fel.pda.financemanagement.ui.utils.ColorUtils.getTotalValueColor @Composable fun TransactionsScreen( @@ -56,21 +59,21 @@ fun TransactionsScreen( factory = AppViewModelProvider.Factory ) ) { - val transactionsUiState: TransactionsUiState by transactionViewModel.transactions.collectAsStateWithLifecycle() + val transactionsUiState: TransactionsWithCategoryUiState by transactionViewModel.transactionsWithCategory.collectAsStateWithLifecycle() Scaffold( topBar = { TopBar() }, content = { innerPadding -> when (transactionsUiState) { - is TransactionsUiState.Loading -> { + is TransactionsWithCategoryUiState.Loading -> { ShowLoadingScreen() } - is TransactionsUiState.Error -> { + is TransactionsWithCategoryUiState.Error -> { ShowErrorScreen( - errorMessage = (transactionsUiState as TransactionsUiState.Error).exception.message ?: "Error", + errorMessage = (transactionsUiState as TransactionsWithCategoryUiState.Error).exception.message ?: "Error", ) } - is TransactionsUiState.Success -> { - TransactionScreenContent( (transactionsUiState as TransactionsUiState.Success).itemList, innerPadding) + is TransactionsWithCategoryUiState.Success -> { + TransactionScreenContent( (transactionsUiState as TransactionsWithCategoryUiState.Success).itemList, innerPadding) } } }, @@ -84,7 +87,7 @@ fun TransactionsScreen( } @Composable -fun TransactionScreenContent(transactions: List<Transaction>, innerPadding: PaddingValues) { +fun TransactionScreenContent(transactions: List<CategoryWithTransaction>, innerPadding: PaddingValues) { Column(modifier = Modifier .padding(innerPadding) .fillMaxWidth()) { @@ -108,23 +111,27 @@ fun TransactionScreenContent(transactions: List<Transaction>, innerPadding: Padd } @Composable -fun TotalSumOfTransactions(transactions: List<Transaction>) { - val totalSum = transactions.sumOf { it.amount }.toString() - Row(modifier = Modifier.fillMaxWidth().padding(14.dp), +fun TotalSumOfTransactions(transactions: List<CategoryWithTransaction>) { + val totalIncome = transactions.filter { it.category.type == CategoryType.INCOME }.sumOf { it.transaction.amount } + val totalExpense = transactions.filter { it.category.type == CategoryType.EXPENSE }.sumOf { it.transaction.amount } + val totalSum = ((totalExpense.times((-1).toBigDecimal())) + totalIncome) + Row(modifier = Modifier + .fillMaxWidth() + .padding(14.dp), horizontalArrangement = Arrangement.Center){ Text(text = "Total: ", style = TextStyle(fontSize = 24.sp, fontWeight = FontWeight.Bold)) Text(text = "$totalSum CZK", style = TextStyle( fontSize = 24.sp, fontWeight = FontWeight.Bold, - color = Color(50, 168, 82) + color = getTotalValueColor(totalSum) ) ) } } @Composable -fun ShowTabListSuccessScreen(itemList: List<Transaction>) { +fun ShowTabListSuccessScreen(itemList: List<CategoryWithTransaction>) { if (itemList.isEmpty()){ Box ( modifier = Modifier.fillMaxSize(), @@ -144,20 +151,20 @@ fun ShowTabListSuccessScreen(itemList: List<Transaction>) { @Composable fun TransactionsList( - itemList: List<Transaction>, + itemList: List<CategoryWithTransaction>, modifier: Modifier = Modifier ) { LazyColumn( modifier = modifier.fillMaxSize(), verticalArrangement = Arrangement.Top ) { - items(itemList) { transaction-> - TransactionItem(transaction) + items(itemList) {transaction -> + TransactionItem(transaction.transaction, transaction.category) } } } @Composable -fun TransactionItem(transaction: Transaction) { +fun TransactionItem(transaction: Transaction, category: Category) { Row( modifier = Modifier .fillMaxWidth() @@ -167,9 +174,12 @@ fun TransactionItem(transaction: Transaction) { , horizontalArrangement = Arrangement.SpaceBetween ) { Column { - Text(text = transaction.description, style = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Bold)) - Text(text = transaction.amount.toString(), style = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Bold)) - } + Text(text = category.name.toString(), style = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Bold)) + Text( + text = if (category.type == CategoryType.EXPENSE) "-${transaction.amount}" else transaction.amount.toString(), + color = getCategoryColor(category), + style = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Bold) + ) } IconButton(onClick = { /* Handle click event here */ }) { Icon(Icons.Filled.MoreVert, contentDescription = "Options") } diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/utils/ColorUtils.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/utils/ColorUtils.kt new file mode 100644 index 0000000..c3ffc91 --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/utils/ColorUtils.kt @@ -0,0 +1,25 @@ +package cz.cvut.fel.pda.financemanagement.ui.utils + +import androidx.compose.ui.graphics.Color +import cz.cvut.fel.pda.financemanagement.database.category.Category +import cz.cvut.fel.pda.financemanagement.database.category.CategoryType +import java.math.BigDecimal + +object ColorUtils { + private val negativeValue = Color(255, 0, 0) + private val positiveValue = Color(50, 168, 82) + fun getCategoryColor(category: Category): Color { + return if (category.type == CategoryType.INCOME) { + positiveValue + } else { + negativeValue + } + } + fun getTotalValueColor(value: BigDecimal): Color { + return if (value >= BigDecimal.ZERO) { + positiveValue + } else { + negativeValue + } + } +} \ No newline at end of file -- GitLab From 8f0493067160e81a147fefeb4ec53ab0022f893f Mon Sep 17 00:00:00 2001 From: dovicrad <dovicrad@fel.cvut.cz> Date: Thu, 25 Apr 2024 10:42:05 +0200 Subject: [PATCH 04/12] :cooking: add transaction detail --- .idea/deploymentTargetDropDown.xml | 3 + .../financemanagement/FinanceManagementApp.kt | 8 ++ .../OfflineTransactionRepository.kt | 2 +- .../database/transaction/TransactionDao.kt | 8 +- .../transaction/TransactionRepository.kt | 18 ++++- .../model/CategoryWithTransaction.kt | 8 +- .../model/TransactionWithCategory.kt | 9 +++ .../transactions/TransactionDetailScreen.kt | 78 +++++++++++++++++++ .../TransactionDetailViewModel.kt | 5 +- .../transactions/TransactionViewModel.kt | 3 +- .../transactions/TransactionsScreen.kt | 26 ++++--- 11 files changed, 147 insertions(+), 21 deletions(-) create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/model/TransactionWithCategory.kt create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailScreen.kt diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml index 9c96b2b..51f9d01 100644 --- a/.idea/deploymentTargetDropDown.xml +++ b/.idea/deploymentTargetDropDown.xml @@ -5,6 +5,9 @@ <entry key="MainActivity"> <State /> </entry> + <entry key="MainActivity (1)"> + <State /> + </entry> <entry key="app"> <State /> </entry> diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApp.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApp.kt index abf31dd..c930012 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApp.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApp.kt @@ -12,11 +12,13 @@ import cz.cvut.fel.pda.financemanagement.ui.screens.AccountsScreen import cz.cvut.fel.pda.financemanagement.ui.screens.CategoriesScreen import cz.cvut.fel.pda.financemanagement.ui.screens.LoansScreen import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.EditTransactionScreen +import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.TransactionDetailScreen import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.TransactionsScreen enum class FinanceManagementScreens() { Transactions, + TransactionsDetail, EditTransaction, Accounts, Categories, @@ -51,5 +53,11 @@ fun FinanceManagementApp() { transitionId = it.arguments?.getLong("Id") ) } + composable(route = "${FinanceManagementScreens.TransactionsDetail.name}/{Id}", + arguments = listOf(navArgument("Id") { type = NavType.LongType })){ + TransactionDetailScreen(navController, + transactionId = it.arguments?.getLong("Id") + ) + } } } \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/OfflineTransactionRepository.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/OfflineTransactionRepository.kt index f5204f7..316a714 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/OfflineTransactionRepository.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/OfflineTransactionRepository.kt @@ -7,7 +7,7 @@ class OfflineTransactionRepository(private val transactionDao: TransactionDao): TransactionRepository { override fun getAllTransactionsStream(): Flow<List<Transaction>> = transactionDao.getAllTransactions() override fun getTransactionForIdStream(id: Long): Flow<Transaction?> = transactionDao.getTransactionForId(id) - override fun getTransactionsWithCategory(): Flow<List<CategoryWithTransaction>> = transactionDao.getTransactionsWithCategory() + override fun getCategoryWithTransactions(): Flow<List<CategoryWithTransaction>> = transactionDao.getCategoryWithTransaction() override suspend fun insertTransactions(vararg transactions: Transaction) = transactionDao.insertTransactions(*transactions) override suspend fun deleteTransaction(transaction: Transaction) = transactionDao.delete(transaction) diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDao.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDao.kt index a47fda2..6e5b40a 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDao.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDao.kt @@ -19,11 +19,11 @@ interface TransactionDao { fun getTransactionForId(id: Long): Flow<Transaction> @Query(""" - SELECT * FROM `transaction` - INNER JOIN `category` ON `transaction`.categoryId = `category`.id - ORDER BY `transaction`.id ASC + SELECT * FROM `category` + INNER JOIN `transaction` ON `transaction`.categoryId = `category`.id + ORDER BY `category`.id ASC """) - fun getTransactionsWithCategory(): Flow<List<CategoryWithTransaction>> + fun getCategoryWithTransaction(): Flow<List<CategoryWithTransaction>> @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertTransactions(vararg transactions: Transaction) diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionRepository.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionRepository.kt index 86f9951..52b4cbd 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionRepository.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionRepository.kt @@ -1,12 +1,28 @@ package cz.cvut.fel.pda.financemanagement.database.transaction +import android.util.Log import cz.cvut.fel.pda.financemanagement.model.CategoryWithTransaction +import cz.cvut.fel.pda.financemanagement.model.TransactionWithCategory import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map interface TransactionRepository { fun getAllTransactionsStream(): Flow<List<Transaction>> fun getTransactionForIdStream(id: Long): Flow<Transaction?> - fun getTransactionsWithCategory(): Flow<List<CategoryWithTransaction>> + + fun getCategoryWithTransactions(): Flow<List<CategoryWithTransaction>> + fun getTransactionsWithCategory(): Flow<List<TransactionWithCategory>> { + return getCategoryWithTransactions().map { categoryWithTransactionList -> + categoryWithTransactionList.flatMap { categoryWithTransaction -> + categoryWithTransaction.transaction.map { transaction -> + TransactionWithCategory( + transaction = transaction, + category = categoryWithTransaction.category + ) + } + } + } + } suspend fun insertTransactions(vararg transactions: Transaction) suspend fun deleteTransaction(transaction: Transaction) suspend fun deleteAllTransactions() diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/CategoryWithTransaction.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/CategoryWithTransaction.kt index 7c31e6f..5d11a1e 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/CategoryWithTransaction.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/CategoryWithTransaction.kt @@ -6,10 +6,10 @@ import cz.cvut.fel.pda.financemanagement.database.category.Category import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction data class CategoryWithTransaction( - @Embedded val transaction: Transaction, + @Embedded val category: Category, @Relation( - parentColumn = "categoryId", - entityColumn = "id" + parentColumn = "id", + entityColumn = "categoryId" ) - val category: Category + val transaction: List<Transaction>, ) \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/TransactionWithCategory.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/TransactionWithCategory.kt new file mode 100644 index 0000000..0e4a422 --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/TransactionWithCategory.kt @@ -0,0 +1,9 @@ +package cz.cvut.fel.pda.financemanagement.model + +import cz.cvut.fel.pda.financemanagement.database.category.Category +import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction + +data class TransactionWithCategory( + val transaction: Transaction, + val category: Category, +) \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailScreen.kt new file mode 100644 index 0000000..ef838f4 --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailScreen.kt @@ -0,0 +1,78 @@ +package cz.cvut.fel.pda.financemanagement.ui.screens.transactions + +import android.provider.Settings.Global.putLong +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.viewmodel.CreationExtras +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavHostController +import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction +import cz.cvut.fel.pda.financemanagement.ui.components.BottomNavigation +import cz.cvut.fel.pda.financemanagement.ui.components.ShowErrorScreen +import cz.cvut.fel.pda.financemanagement.ui.components.ShowLoadingScreen +import cz.cvut.fel.pda.financemanagement.ui.components.TopBar +import cz.cvut.fel.pda.financemanagement.ui.utils.AppViewModelProvider + + +@Composable +fun TransactionDetailScreen( + navController: NavHostController, + transactionId: Long?, + transactionViewModel: TransactionDetailViewModel = viewModel( + factory = AppViewModelProvider.Factory + ) +){ + transactionViewModel.setTransactionId(transactionId ?: 0L) + val transactionUiState: TransactionUiState by transactionViewModel.uiState.collectAsStateWithLifecycle() + + Scaffold ( + topBar = {TopBar()}, + content = { + innerPadding -> + when (transactionUiState) { + is TransactionUiState.Loading -> { + ShowLoadingScreen() + } + is TransactionUiState.Error -> { + ShowErrorScreen( + errorMessage = (transactionUiState as TransactionUiState.Error).exception.message ?: "Error", + ) + } + is TransactionUiState.Success -> { + TransactionScreenContent( (transactionUiState as TransactionUiState.Success).transaction, innerPadding, navController) + } + } + }, + bottomBar = { + BottomNavigation(navController = navController) + } + ) +} + +@Composable +fun TransactionScreenContent(transaction: Transaction, innerPadding: PaddingValues, navController: NavHostController) { + Column(modifier = Modifier.padding(innerPadding)) { + Text(text = "Transaction Details") + Spacer(modifier = Modifier.height(16.dp)) + Row { + Text(text = "ID: ") + Text(text = transaction.id.toString()) + } + Row { + Text(text = "Amount: ") + Text(text = transaction.amount.toString()) + } + // Add more fields as needed + } +} \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailViewModel.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailViewModel.kt index 1d34f03..ce6080c 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailViewModel.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailViewModel.kt @@ -16,7 +16,7 @@ class TransactionDetailViewModel( private val transactionRepository: TransactionRepository, savedStateHandle: SavedStateHandle ): ViewModel() { - private val transactionId: Long = savedStateHandle.get<Long>("Id")!! + private var transactionId: Long = savedStateHandle.get<Long>("Id")!! val uiState: StateFlow<TransactionUiState> = transactionRepository @@ -34,6 +34,9 @@ class TransactionDetailViewModel( started = SharingStarted.WhileSubscribed(5000L), initialValue = TransactionUiState.Loading ) + fun setTransactionId(id: Long) { + transactionId = id + } fun saveTransaction( amount: BigDecimal ) { diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionViewModel.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionViewModel.kt index f41519f..0851eaa 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionViewModel.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import cz.cvut.fel.pda.financemanagement.database.transaction.TransactionRepository import cz.cvut.fel.pda.financemanagement.model.CategoryWithTransaction +import cz.cvut.fel.pda.financemanagement.model.TransactionWithCategory import cz.cvut.fel.pda.financemanagement.model.common.Result import cz.cvut.fel.pda.financemanagement.model.common.asResult import kotlinx.coroutines.flow.SharingStarted @@ -33,6 +34,6 @@ class TransactionViewModel( sealed interface TransactionsWithCategoryUiState { data object Loading : TransactionsWithCategoryUiState - data class Success(val itemList: List<CategoryWithTransaction> = listOf()) : TransactionsWithCategoryUiState + data class Success(val itemList: List<TransactionWithCategory> = listOf()) : TransactionsWithCategoryUiState data class Error(val exception: Exception) : TransactionsWithCategoryUiState } \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt index 925275d..c39b30b 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt @@ -1,6 +1,7 @@ package cz.cvut.fel.pda.financemanagement.ui.screens.transactions import androidx.compose.foundation.border +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -43,6 +44,7 @@ import cz.cvut.fel.pda.financemanagement.database.category.Category import cz.cvut.fel.pda.financemanagement.database.category.CategoryType import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction import cz.cvut.fel.pda.financemanagement.model.CategoryWithTransaction +import cz.cvut.fel.pda.financemanagement.model.TransactionWithCategory import cz.cvut.fel.pda.financemanagement.ui.components.BottomNavigation import cz.cvut.fel.pda.financemanagement.ui.components.ShowErrorScreen import cz.cvut.fel.pda.financemanagement.ui.components.ShowLoadingScreen @@ -73,7 +75,7 @@ fun TransactionsScreen( ) } is TransactionsWithCategoryUiState.Success -> { - TransactionScreenContent( (transactionsUiState as TransactionsWithCategoryUiState.Success).itemList, innerPadding) + TransactionScreenContent( (transactionsUiState as TransactionsWithCategoryUiState.Success).itemList,navController, innerPadding) } } }, @@ -87,7 +89,11 @@ fun TransactionsScreen( } @Composable -fun TransactionScreenContent(transactions: List<CategoryWithTransaction>, innerPadding: PaddingValues) { +fun TransactionScreenContent( + transactions: List<TransactionWithCategory>, + navController: NavHostController, + innerPadding: PaddingValues +) { Column(modifier = Modifier .padding(innerPadding) .fillMaxWidth()) { @@ -105,13 +111,13 @@ fun TransactionScreenContent(transactions: List<CategoryWithTransaction>, innerP } HorizontalDivider() - ShowTabListSuccessScreen(transactions) + ShowTabListSuccessScreen(transactions, navController) } } } @Composable -fun TotalSumOfTransactions(transactions: List<CategoryWithTransaction>) { +fun TotalSumOfTransactions(transactions: List<TransactionWithCategory>) { val totalIncome = transactions.filter { it.category.type == CategoryType.INCOME }.sumOf { it.transaction.amount } val totalExpense = transactions.filter { it.category.type == CategoryType.EXPENSE }.sumOf { it.transaction.amount } val totalSum = ((totalExpense.times((-1).toBigDecimal())) + totalIncome) @@ -131,7 +137,7 @@ fun TotalSumOfTransactions(transactions: List<CategoryWithTransaction>) { } @Composable -fun ShowTabListSuccessScreen(itemList: List<CategoryWithTransaction>) { +fun ShowTabListSuccessScreen(itemList: List<TransactionWithCategory>, navController: NavHostController) { if (itemList.isEmpty()){ Box ( modifier = Modifier.fillMaxSize(), @@ -145,13 +151,14 @@ fun ShowTabListSuccessScreen(itemList: List<CategoryWithTransaction>) { ) } } else { - TransactionsList(itemList) + TransactionsList(itemList, navController) } } @Composable fun TransactionsList( - itemList: List<CategoryWithTransaction>, + itemList: List<TransactionWithCategory>, + navController: NavHostController, modifier: Modifier = Modifier ) { LazyColumn( @@ -159,17 +166,18 @@ fun TransactionsList( verticalArrangement = Arrangement.Top ) { items(itemList) {transaction -> - TransactionItem(transaction.transaction, transaction.category) + TransactionItem(transaction.transaction, transaction.category, navController) } } } @Composable -fun TransactionItem(transaction: Transaction, category: Category) { +fun TransactionItem(transaction: Transaction, category: Category, navController: NavHostController) { Row( modifier = Modifier .fillMaxWidth() .padding(vertical = 8.dp) .border(1.dp, Color.Gray, shape = RoundedCornerShape(3.dp)) + .clickable { navController.navigate("${FinanceManagementScreens.TransactionsDetail.name}/${transaction.id}") } .padding(8.dp) , horizontalArrangement = Arrangement.SpaceBetween ) { -- GitLab From 32f2488fabb6324b0949f2e6e16b74cc1d4639ed Mon Sep 17 00:00:00 2001 From: dovicrad <dovicrad@fel.cvut.cz> Date: Thu, 25 Apr 2024 12:14:12 +0200 Subject: [PATCH 05/12] :cooking: add transaction sorting --- .../financemanagement/database/AppDatabase.kt | 2 +- .../database/transaction/Operation.kt | 17 +++++++ .../database/transaction/Transaction.kt | 14 +++--- .../database/typeConvertion/Converters.kt | 12 +++++ .../financemanagement/ui/components/TopBar.kt | 2 +- .../transactions/EditTransactionScreen.kt | 10 ++-- .../transactions/TransactionsScreen.kt | 48 +++++++++++++++---- 7 files changed, 85 insertions(+), 20 deletions(-) create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/Operation.kt diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/AppDatabase.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/AppDatabase.kt index 7abdd45..ac315bf 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/AppDatabase.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/AppDatabase.kt @@ -11,7 +11,7 @@ import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction import cz.cvut.fel.pda.financemanagement.database.transaction.TransactionDao import cz.cvut.fel.pda.financemanagement.database.typeConvertion.Converters -@Database(entities = [Transaction::class, Category::class], version = 6, exportSchema = false) +@Database(entities = [Transaction::class, Category::class], version = 7, exportSchema = false) @TypeConverters(Converters::class) abstract class AppDatabase : RoomDatabase(){ abstract fun transactionDao(): TransactionDao diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/Operation.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/Operation.kt new file mode 100644 index 0000000..5618432 --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/Operation.kt @@ -0,0 +1,17 @@ +package cz.cvut.fel.pda.financemanagement.database.transaction; + +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +import java.math.BigDecimal; +import java.time.LocalDateTime +import java.util.Date + +@Entity(tableName = "operation") +abstract class Operation { + @PrimaryKey(autoGenerate = true) + open var id: Long = 0 + abstract var description: String + abstract var amount: BigDecimal + abstract var dateCreated: LocalDateTime +} \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/Transaction.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/Transaction.kt index 5486dd5..5042f03 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/Transaction.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/Transaction.kt @@ -3,14 +3,16 @@ package cz.cvut.fel.pda.financemanagement.database.transaction import androidx.room.Entity import androidx.room.PrimaryKey import java.math.BigDecimal +import java.time.LocalDateTime @Entity(tableName = "transaction") data class Transaction( @PrimaryKey(autoGenerate = true) - val id: Long = 0, - val description: String = "", - val imageUrl: String = "", - val amount: BigDecimal = BigDecimal.ZERO, - val categoryId: Long, -) + override var id: Long = 0, + override var description: String = "", + override var amount: BigDecimal = BigDecimal.ZERO, + override var dateCreated: LocalDateTime = LocalDateTime.now(), + var imageUrl: String = "", + var categoryId: Long, +) : Operation() diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/typeConvertion/Converters.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/typeConvertion/Converters.kt index 23841c0..c6ca942 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/typeConvertion/Converters.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/typeConvertion/Converters.kt @@ -2,6 +2,8 @@ package cz.cvut.fel.pda.financemanagement.database.typeConvertion import androidx.room.TypeConverter import java.math.BigDecimal +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter class Converters { @TypeConverter @@ -13,4 +15,14 @@ class Converters { fun toBigDecimal(value: String): BigDecimal { return BigDecimal(value) } + + @TypeConverter + fun fromLocalDateTime(value: LocalDateTime): String { + return value.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) + } + + @TypeConverter + fun toLocalDateTime(value: String): LocalDateTime { + return LocalDateTime.parse(value, DateTimeFormatter.ISO_LOCAL_DATE_TIME) + } } \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/TopBar.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/TopBar.kt index 3512327..4472df4 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/TopBar.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/TopBar.kt @@ -22,7 +22,7 @@ fun TopBar() { } Text( - text = "Money management app", + text = "CoinKeeper", style = TextStyle(fontSize = 24.sp, fontWeight = FontWeight.Bold), modifier = Modifier.align(Alignment.Center) ) diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/EditTransactionScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/EditTransactionScreen.kt index 463bd58..1b692a3 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/EditTransactionScreen.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/EditTransactionScreen.kt @@ -76,7 +76,7 @@ fun ShowSuccessScreen( Scaffold( topBar = { TopAppBar( - title = { Text("neco") }, + title = { Text("Transaction edit") }, colors = TopAppBarDefaults.mediumTopAppBarColors( containerColor = MaterialTheme.colorScheme.primaryContainer ), @@ -125,8 +125,12 @@ fun EditTransactionDetailSuccessContent(innerPadding: PaddingValues, amount: Mut .padding(24.dp, 4.dp) OutlinedTextField( value = amount.value, - onValueChange = { newValue -> amount.value = newValue }, - label = { Text("xd") }, + onValueChange = { newValue -> + if (newValue.all { it.isDigit() }) { + amount.value = newValue + } + }, + label = { Text("amount") }, modifier = spaceModifier ) } diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt index c39b30b..eb2de35 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt @@ -17,6 +17,8 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowDropDown import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.outlined.Add +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon @@ -25,7 +27,11 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState 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.graphics.Color @@ -43,7 +49,6 @@ import cz.cvut.fel.pda.financemanagement.FinanceManagementScreens import cz.cvut.fel.pda.financemanagement.database.category.Category import cz.cvut.fel.pda.financemanagement.database.category.CategoryType import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction -import cz.cvut.fel.pda.financemanagement.model.CategoryWithTransaction import cz.cvut.fel.pda.financemanagement.model.TransactionWithCategory import cz.cvut.fel.pda.financemanagement.ui.components.BottomNavigation import cz.cvut.fel.pda.financemanagement.ui.components.ShowErrorScreen @@ -94,26 +99,51 @@ fun TransactionScreenContent( navController: NavHostController, innerPadding: PaddingValues ) { + val sortState = remember { mutableStateOf("Don't sort") } + val sortedTransactions = when (sortState.value) { + "Sort by Amount" -> transactions.sortedBy { it.transaction.amount } + "Sort by Date" -> transactions.sortedBy { it.transaction.dateCreated } + else -> transactions + } + Column(modifier = Modifier .padding(innerPadding) .fillMaxWidth()) { - TotalSumOfTransactions(transactions) + TotalSumOfTransactions(sortedTransactions) Column(modifier = Modifier.padding(horizontal = 14.dp)) { Row(modifier = Modifier .fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween ){ Text(text = "Transactions", style = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Bold)) - Row{ - Text(text = "sort") - Icon(Icons.Filled.ArrowDropDown, contentDescription = "Sort options") - } - + SortDropdown(sortState) } HorizontalDivider() - ShowTabListSuccessScreen(transactions, navController) + ShowTabListSuccessScreen(sortedTransactions, navController) + } + } +} + +@Composable +fun SortDropdown(sortState: MutableState<String>) { + var expanded by remember { mutableStateOf(false) } + val options = listOf("Don't sort", "Sort by Amount", "Sort by Date") + + Box(Modifier.clickable { expanded = true }) { + Text(text = sortState.value) + Icon(Icons.Filled.ArrowDropDown, contentDescription = "Sort options") + DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) { + options.forEach { label -> + DropdownMenuItem( + onClick = { + sortState.value = label + expanded = false + }, + text = { Text(label) } + ) + } } - } + } } @Composable -- GitLab From 9dc5f01aa3cb7bd8f59226cb439cbac2a929c495 Mon Sep 17 00:00:00 2001 From: dovicrad <dovicrad@fel.cvut.cz> Date: Thu, 25 Apr 2024 14:28:24 +0200 Subject: [PATCH 06/12] :cooking: add categories display --- .../financemanagement/FinanceManagementApp.kt | 2 +- .../database/category/CategoryRepository.kt | 2 +- .../category/OfflineCategoryRepository.kt | 13 +++ .../OfflineTransactionRepository.kt | 3 + .../database/transaction/TransactionDao.kt | 7 ++ .../transaction/TransactionRepository.kt | 12 +- .../pda/financemanagement/di/AppContainer.kt | 6 + .../ui/screens/CategoriesScreen.kt | 33 ------ .../ui/screens/cateegory/CategoriesScreen.kt | 105 ++++++++++++++++++ .../ui/screens/cateegory/CategoryViewModel.kt | 38 +++++++ .../transactions/EditTransactionScreen.kt | 11 +- .../transactions/TransactionDetailScreen.kt | 31 ++++-- .../TransactionDetailViewModel.kt | 29 +++-- .../transactions/TransactionViewModel.kt | 3 +- .../ui/utils/AppViewModelProvider.kt | 6 + 15 files changed, 239 insertions(+), 62 deletions(-) create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/database/category/OfflineCategoryRepository.kt delete mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/CategoriesScreen.kt create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoriesScreen.kt create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoryViewModel.kt diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApp.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApp.kt index c930012..4cc1431 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApp.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApp.kt @@ -9,7 +9,7 @@ import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument import cz.cvut.fel.pda.financemanagement.ui.screens.AccountsScreen -import cz.cvut.fel.pda.financemanagement.ui.screens.CategoriesScreen +import cz.cvut.fel.pda.financemanagement.ui.screens.cateegory.CategoriesScreen import cz.cvut.fel.pda.financemanagement.ui.screens.LoansScreen import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.EditTransactionScreen import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.TransactionDetailScreen diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/category/CategoryRepository.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/category/CategoryRepository.kt index cfd96e9..1e1538e 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/category/CategoryRepository.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/category/CategoryRepository.kt @@ -8,5 +8,5 @@ interface CategoryRepository { suspend fun insertCategories(vararg categories: Category) suspend fun deleteCategory(category: Category) suspend fun deleteAllCategories() - suspend fun updateCategory(vararg categories: Category) + suspend fun updateCategory(vararg category: Category) } \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/category/OfflineCategoryRepository.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/category/OfflineCategoryRepository.kt new file mode 100644 index 0000000..0b83212 --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/category/OfflineCategoryRepository.kt @@ -0,0 +1,13 @@ +package cz.cvut.fel.pda.financemanagement.database.category + +import kotlinx.coroutines.flow.Flow + +class OfflineCategoryRepository(private val CategoryDao: CategoryDao): + CategoryRepository { + override fun getAllCategoriesStream(): Flow<List<Category>> = CategoryDao.getAllCategories() + override fun getCategoryForIdStream(id: Long): Flow<Category?> = CategoryDao.getCategoryForId(id) + override suspend fun insertCategories(vararg categories: Category) = CategoryDao.insertCategories(*categories) + override suspend fun deleteCategory(category: Category) = CategoryDao.delete(category) + override suspend fun deleteAllCategories() = CategoryDao.deleteAll() + override suspend fun updateCategory(vararg category: Category) = CategoryDao.updateCategories(*category) +} \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/OfflineTransactionRepository.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/OfflineTransactionRepository.kt index 316a714..402b2e9 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/OfflineTransactionRepository.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/OfflineTransactionRepository.kt @@ -1,12 +1,15 @@ package cz.cvut.fel.pda.financemanagement.database.transaction import cz.cvut.fel.pda.financemanagement.model.CategoryWithTransaction +import cz.cvut.fel.pda.financemanagement.model.TransactionWithCategory import kotlinx.coroutines.flow.Flow class OfflineTransactionRepository(private val transactionDao: TransactionDao): TransactionRepository { override fun getAllTransactionsStream(): Flow<List<Transaction>> = transactionDao.getAllTransactions() override fun getTransactionForIdStream(id: Long): Flow<Transaction?> = transactionDao.getTransactionForId(id) + override fun getCategoryWithTransactionForIdStream(id: Long): Flow<CategoryWithTransaction> = transactionDao.getCategoryWithTransactionForId(id) + override fun getCategoryWithTransactions(): Flow<List<CategoryWithTransaction>> = transactionDao.getCategoryWithTransaction() override suspend fun insertTransactions(vararg transactions: Transaction) = transactionDao.insertTransactions(*transactions) diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDao.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDao.kt index 6e5b40a..ae82277 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDao.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDao.kt @@ -18,6 +18,13 @@ interface TransactionDao { @Query("SELECT * FROM `transaction` WHERE id = :id") fun getTransactionForId(id: Long): Flow<Transaction> + @Query(""" + SELECT * FROM `category` + JOIN `transaction` ON `transaction`.categoryId = `category`.id + WHERE `transaction`.id = :id +""") + fun getCategoryWithTransactionForId(id: Long): Flow<CategoryWithTransaction> + @Query(""" SELECT * FROM `category` INNER JOIN `transaction` ON `transaction`.categoryId = `category`.id diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionRepository.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionRepository.kt index 52b4cbd..e900a4c 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionRepository.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionRepository.kt @@ -1,6 +1,5 @@ package cz.cvut.fel.pda.financemanagement.database.transaction -import android.util.Log import cz.cvut.fel.pda.financemanagement.model.CategoryWithTransaction import cz.cvut.fel.pda.financemanagement.model.TransactionWithCategory import kotlinx.coroutines.flow.Flow @@ -9,6 +8,17 @@ import kotlinx.coroutines.flow.map interface TransactionRepository { fun getAllTransactionsStream(): Flow<List<Transaction>> fun getTransactionForIdStream(id: Long): Flow<Transaction?> + fun getTransactionWithCategoryForIdStream(id: Long): Flow<TransactionWithCategory?> { + return getCategoryWithTransactionForIdStream(id).map { transaction -> + transaction.let { + TransactionWithCategory( + transaction = it.transaction.firstOrNull { transaction -> transaction.id == id } ?: Transaction(categoryId = 0L), + category = it.category + ) + } + } + } + fun getCategoryWithTransactionForIdStream(id: Long): Flow<CategoryWithTransaction> fun getCategoryWithTransactions(): Flow<List<CategoryWithTransaction>> fun getTransactionsWithCategory(): Flow<List<TransactionWithCategory>> { diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/di/AppContainer.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/di/AppContainer.kt index fdc8ec7..7ee3744 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/di/AppContainer.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/di/AppContainer.kt @@ -2,15 +2,21 @@ package cz.cvut.fel.pda.financemanagement.di import android.content.Context import cz.cvut.fel.pda.financemanagement.database.AppDatabase +import cz.cvut.fel.pda.financemanagement.database.category.CategoryRepository +import cz.cvut.fel.pda.financemanagement.database.category.OfflineCategoryRepository import cz.cvut.fel.pda.financemanagement.database.transaction.OfflineTransactionRepository import cz.cvut.fel.pda.financemanagement.database.transaction.TransactionRepository interface AppContainer { val transactionRepository: TransactionRepository + val categoryRepository: CategoryRepository } class AppDataContainer(private val context: Context) : AppContainer { override val transactionRepository: TransactionRepository by lazy { OfflineTransactionRepository(AppDatabase.getDatabase(context).transactionDao()) } + override val categoryRepository: CategoryRepository by lazy { + OfflineCategoryRepository(AppDatabase.getDatabase(context).categoryDao()) + } } \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/CategoriesScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/CategoriesScreen.kt deleted file mode 100644 index 10fefbc..0000000 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/CategoriesScreen.kt +++ /dev/null @@ -1,33 +0,0 @@ -package cz.cvut.fel.pda.financemanagement.ui.screens - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.navigation.NavHostController -import cz.cvut.fel.pda.financemanagement.ui.components.BottomNavigation -import cz.cvut.fel.pda.financemanagement.ui.components.TopBar - -@Composable -fun CategoriesScreen(navController: NavHostController){ - Scaffold( - topBar = { - TopBar() - }, - bottomBar = { - BottomNavigation(navController = navController) - } - ) { - innerPadding -> LoansScreenContent(innerPadding) - } -} - -@Composable -fun LoansScreenContent(innerPadding: PaddingValues) { - Column(modifier = Modifier.padding(innerPadding)) { - Text(text = "Categories") - } -} diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoriesScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoriesScreen.kt new file mode 100644 index 0000000..fc7a6c1 --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoriesScreen.kt @@ -0,0 +1,105 @@ +package cz.cvut.fel.pda.financemanagement.ui.screens.cateegory + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavHostController +import cz.cvut.fel.pda.financemanagement.database.category.Category +import cz.cvut.fel.pda.financemanagement.database.category.CategoryType +import cz.cvut.fel.pda.financemanagement.ui.components.BottomNavigation +import cz.cvut.fel.pda.financemanagement.ui.components.ShowErrorScreen +import cz.cvut.fel.pda.financemanagement.ui.components.ShowLoadingScreen +import cz.cvut.fel.pda.financemanagement.ui.components.TopBar +import cz.cvut.fel.pda.financemanagement.ui.utils.AppViewModelProvider +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.HorizontalDivider +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import java.util.Locale + +@Composable +fun CategoriesScreen( + navController: NavHostController, + categoryViewModel: CategoryViewModel = viewModel( + factory = AppViewModelProvider.Factory + ) +){ + val categoriesUiState: CategoriesUiState by categoryViewModel.categories.collectAsStateWithLifecycle() + + Scaffold( + topBar = { + TopBar() + }, + content = { + innerPadding -> + when (categoriesUiState) { + is CategoriesUiState.Loading -> { + ShowLoadingScreen() + } + is CategoriesUiState.Error -> { + ShowErrorScreen( + errorMessage = (categoriesUiState as CategoriesUiState.Error).exception.message ?: "Error", + ) + } + is CategoriesUiState.Success -> { + CategoriesScreenContent((categoriesUiState as CategoriesUiState.Success).itemList,navController, innerPadding) + } + } + }, + floatingActionButton = { + FloatingActionButton(onClick = { }) { + Icon(Icons.Default.Add, contentDescription = "Add") + } + + }, + bottomBar = { + BottomNavigation(navController = navController) + } + ) +} + +@Composable +fun CategoriesScreenContent(categories: List<Category>, navController: NavHostController, innerPadding: PaddingValues) { + val expenseCategories = categories.filter { it.type == CategoryType.EXPENSE } + val incomeCategories = categories.filter { it.type == CategoryType.INCOME } + + Column(modifier = Modifier.padding(innerPadding).padding(horizontal = 14.dp)) { + Text(text = "Expense Categories:", style = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Bold)) + HorizontalDivider() + CategoriesList(expenseCategories) + Text(text = "Income Categories:", style = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Bold)) + HorizontalDivider() + CategoriesList(incomeCategories) + } +} + +@Composable +fun CategoriesList(categories: List<Category>) { + LazyColumn { + items(categories) { category -> + CategoryItem(category) + } + } +} + +@Composable +fun CategoryItem(category: Category) { + Row { + Text(text = category.name.toString().lowercase().replaceFirstChar { it.titlecase(Locale.ROOT)}) + } +} diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoryViewModel.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoryViewModel.kt new file mode 100644 index 0000000..4777dfb --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoryViewModel.kt @@ -0,0 +1,38 @@ +package cz.cvut.fel.pda.financemanagement.ui.screens.cateegory + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import cz.cvut.fel.pda.financemanagement.database.category.Category +import cz.cvut.fel.pda.financemanagement.database.category.CategoryRepository +import cz.cvut.fel.pda.financemanagement.model.common.Result +import cz.cvut.fel.pda.financemanagement.model.common.asResult +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn + +class CategoryViewModel( + categoryRepository: CategoryRepository, +) : ViewModel(){ + val categories : StateFlow<CategoriesUiState> = + categoryRepository + .getAllCategoriesStream() + .asResult() + .map { allCategoriesToResult -> + when ( allCategoriesToResult) { + is Result.Error -> CategoriesUiState.Error(Exception( allCategoriesToResult.exception)) + is Result.Loading -> CategoriesUiState.Loading + is Result.Success -> CategoriesUiState.Success( allCategoriesToResult.data) + } + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000L), + initialValue = CategoriesUiState.Loading + ) +} + +sealed interface CategoriesUiState { + data object Loading : CategoriesUiState + data class Success(val itemList: List<Category> = listOf()) : CategoriesUiState + data class Error(val exception: Exception) : CategoriesUiState +} \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/EditTransactionScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/EditTransactionScreen.kt index 1b692a3..5ea69ae 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/EditTransactionScreen.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/EditTransactionScreen.kt @@ -31,6 +31,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction +import cz.cvut.fel.pda.financemanagement.model.TransactionWithCategory import cz.cvut.fel.pda.financemanagement.ui.components.ShowErrorScreen import cz.cvut.fel.pda.financemanagement.ui.components.ShowLoadingScreen import cz.cvut.fel.pda.financemanagement.ui.utils.AppViewModelProvider @@ -45,16 +46,16 @@ fun EditTransactionScreen(navController: NavHostController, val uiState by transitionDetailViewModel.uiState.collectAsStateWithLifecycle() when (val state = uiState) { - is TransactionUiState.Error -> { + is TransactionWithCategoryUiState.Error -> { ShowErrorScreen( errorMessage = state.exception.message ?: "Error", onClick = { navController.popBackStack() } ) } - TransactionUiState.Loading -> { + TransactionWithCategoryUiState.Loading -> { ShowLoadingScreen() } - is TransactionUiState.Success -> { + is TransactionWithCategoryUiState.Success -> { ShowSuccessScreen( state.transaction, navController, @@ -67,11 +68,11 @@ fun EditTransactionScreen(navController: NavHostController, @OptIn(ExperimentalMaterial3Api::class) @Composable fun ShowSuccessScreen( - transaction: Transaction, + transaction: TransactionWithCategory, navController: NavHostController, transactionViewModel: TransactionDetailViewModel ) { - val amount = rememberSaveable { mutableStateOf(transaction.amount.toString()) } + val amount = rememberSaveable { mutableStateOf(transaction.transaction.amount.toString()) } Scaffold( topBar = { diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailScreen.kt index ef838f4..2ed972e 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailScreen.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailScreen.kt @@ -18,6 +18,7 @@ import androidx.lifecycle.viewmodel.CreationExtras import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction +import cz.cvut.fel.pda.financemanagement.model.TransactionWithCategory import cz.cvut.fel.pda.financemanagement.ui.components.BottomNavigation import cz.cvut.fel.pda.financemanagement.ui.components.ShowErrorScreen import cz.cvut.fel.pda.financemanagement.ui.components.ShowLoadingScreen @@ -34,23 +35,23 @@ fun TransactionDetailScreen( ) ){ transactionViewModel.setTransactionId(transactionId ?: 0L) - val transactionUiState: TransactionUiState by transactionViewModel.uiState.collectAsStateWithLifecycle() + val transactionUiState: TransactionWithCategoryUiState by transactionViewModel.uiState.collectAsStateWithLifecycle() Scaffold ( topBar = {TopBar()}, content = { innerPadding -> when (transactionUiState) { - is TransactionUiState.Loading -> { + is TransactionWithCategoryUiState.Loading -> { ShowLoadingScreen() } - is TransactionUiState.Error -> { + is TransactionWithCategoryUiState.Error -> { ShowErrorScreen( - errorMessage = (transactionUiState as TransactionUiState.Error).exception.message ?: "Error", + errorMessage = (transactionUiState as TransactionWithCategoryUiState.Error).exception.message ?: "Error", ) } - is TransactionUiState.Success -> { - TransactionScreenContent( (transactionUiState as TransactionUiState.Success).transaction, innerPadding, navController) + is TransactionWithCategoryUiState.Success -> { + TransactionScreenContent( (transactionUiState as TransactionWithCategoryUiState.Success).transaction, innerPadding, navController) } } }, @@ -61,17 +62,29 @@ fun TransactionDetailScreen( } @Composable -fun TransactionScreenContent(transaction: Transaction, innerPadding: PaddingValues, navController: NavHostController) { +fun TransactionScreenContent(transaction: TransactionWithCategory, innerPadding: PaddingValues, navController: NavHostController) { Column(modifier = Modifier.padding(innerPadding)) { Text(text = "Transaction Details") Spacer(modifier = Modifier.height(16.dp)) Row { Text(text = "ID: ") - Text(text = transaction.id.toString()) + Text(text = transaction.transaction.id.toString()) + } + Row { + Text(text = "Created: ") + Text(text = transaction.transaction.dateCreated.toString()) } Row { Text(text = "Amount: ") - Text(text = transaction.amount.toString()) + Text(text = transaction.transaction.amount.toString()) + } + Row { + Text(text = "Category name: ") + Text(text = transaction.category.name.toString()) + } + Row { + Text(text = "Category type: ") + Text(text = transaction.category.type.toString()) } // Add more fields as needed } diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailViewModel.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailViewModel.kt index ce6080c..a9eceea 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailViewModel.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailViewModel.kt @@ -3,8 +3,12 @@ package cz.cvut.fel.pda.financemanagement.ui.screens.transactions import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import cz.cvut.fel.pda.financemanagement.database.category.Category +import cz.cvut.fel.pda.financemanagement.database.category.CategoryName +import cz.cvut.fel.pda.financemanagement.database.category.CategoryType import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction import cz.cvut.fel.pda.financemanagement.database.transaction.TransactionRepository +import cz.cvut.fel.pda.financemanagement.model.TransactionWithCategory import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map @@ -18,21 +22,26 @@ class TransactionDetailViewModel( ): ViewModel() { private var transactionId: Long = savedStateHandle.get<Long>("Id")!! - val uiState: StateFlow<TransactionUiState> = + val uiState: StateFlow<TransactionWithCategoryUiState> = transactionRepository - .getTransactionForIdStream(transactionId) + .getTransactionWithCategoryForIdStream(transactionId) .map { if (it != null) { - TransactionUiState.Success(it) + TransactionWithCategoryUiState.Success(it) } else if (transactionId == 0L) { - TransactionUiState.Success(Transaction(categoryId = 0L)) + TransactionWithCategoryUiState.Success( + TransactionWithCategory( + transaction = Transaction(categoryId = 0L), + category = Category(0, CategoryName.GROCERIES,CategoryType.INCOME) + ) + ) } else { - TransactionUiState.Error(Exception("Transaction not found")) + TransactionWithCategoryUiState.Error(Exception("Transaction not found")) } }.stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5000L), - initialValue = TransactionUiState.Loading + initialValue = TransactionWithCategoryUiState.Loading ) fun setTransactionId(id: Long) { transactionId = id @@ -52,8 +61,8 @@ class TransactionDetailViewModel( } } -sealed interface TransactionUiState { - data object Loading : TransactionUiState - data class Success(val transaction: Transaction) : TransactionUiState - data class Error(val exception: Exception) : TransactionUiState +sealed interface TransactionWithCategoryUiState { + data object Loading : TransactionWithCategoryUiState + data class Success(val transaction: TransactionWithCategory) : TransactionWithCategoryUiState + data class Error(val exception: Exception) : TransactionWithCategoryUiState } \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionViewModel.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionViewModel.kt index 0851eaa..b90f770 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionViewModel.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionViewModel.kt @@ -3,7 +3,6 @@ package cz.cvut.fel.pda.financemanagement.ui.screens.transactions import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import cz.cvut.fel.pda.financemanagement.database.transaction.TransactionRepository -import cz.cvut.fel.pda.financemanagement.model.CategoryWithTransaction import cz.cvut.fel.pda.financemanagement.model.TransactionWithCategory import cz.cvut.fel.pda.financemanagement.model.common.Result import cz.cvut.fel.pda.financemanagement.model.common.asResult @@ -13,7 +12,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn class TransactionViewModel( - private val transactionRepository: TransactionRepository, + transactionRepository: TransactionRepository, ) : ViewModel(){ val transactionsWithCategory : StateFlow<TransactionsWithCategoryUiState> = transactionRepository diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/utils/AppViewModelProvider.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/utils/AppViewModelProvider.kt index 696af66..f854683 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/utils/AppViewModelProvider.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/utils/AppViewModelProvider.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.viewmodel.CreationExtras import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory import cz.cvut.fel.pda.financemanagement.FinanceManagementApplication +import cz.cvut.fel.pda.financemanagement.ui.screens.cateegory.CategoryViewModel import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.TransactionDetailViewModel import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.TransactionViewModel @@ -22,6 +23,11 @@ object AppViewModelProvider { this.createSavedStateHandle() ) } + initializer { + CategoryViewModel( + financemanagementApplication().container.categoryRepository, + ) + } } } -- GitLab From c9256fc28fb4c699c22e4528dbfea437e462a935 Mon Sep 17 00:00:00 2001 From: dovicrad <dovicrad@fel.cvut.cz> Date: Mon, 29 Apr 2024 18:02:19 +0200 Subject: [PATCH 07/12] :cooking: FIX TRANSACTION WITH CATEGORY (hopefully) !!!! --- .../financemanagement/FinanceManagementApp.kt | 12 +++- .../OfflineTransactionRepository.kt | 2 +- .../database/transaction/TransactionDao.kt | 3 +- .../transaction/TransactionRepository.kt | 12 +--- .../model/TransactionWithCategory.kt | 8 ++- .../ui/components/SideSheet.kt | 14 ++++ .../financemanagement/ui/components/TopBar.kt | 8 ++- .../ui/screens/AccountsScreen.kt | 2 +- .../ui/screens/LoansScreen.kt | 2 +- .../ui/screens/cateegory/CategoriesScreen.kt | 6 +- .../cateegory/CategoryDetailViewModel.kt | 66 +++++++++++++++++++ .../screens/cateegory/EditCategoryScreen.kt | 52 +++++++++++++++ .../transactions/TransactionDetailScreen.kt | 2 +- .../transactions/TransactionsScreen.kt | 2 +- .../ui/utils/AppViewModelProvider.kt | 7 ++ 15 files changed, 175 insertions(+), 23 deletions(-) create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/SideSheet.kt create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoryDetailViewModel.kt create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/EditCategoryScreen.kt diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApp.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApp.kt index 4cc1431..d7c98c5 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApp.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApp.kt @@ -11,6 +11,7 @@ import androidx.navigation.navArgument import cz.cvut.fel.pda.financemanagement.ui.screens.AccountsScreen import cz.cvut.fel.pda.financemanagement.ui.screens.cateegory.CategoriesScreen import cz.cvut.fel.pda.financemanagement.ui.screens.LoansScreen +import cz.cvut.fel.pda.financemanagement.ui.screens.cateegory.EditCategoryScreen import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.EditTransactionScreen import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.TransactionDetailScreen import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.TransactionsScreen @@ -20,8 +21,11 @@ enum class FinanceManagementScreens() { Transactions, TransactionsDetail, EditTransaction, - Accounts, + Categories, + EditCategory, + + Accounts, Loans } @@ -59,5 +63,11 @@ fun FinanceManagementApp() { transactionId = it.arguments?.getLong("Id") ) } + composable(route = "${FinanceManagementScreens.EditCategory.name}/{Id}", + arguments = listOf(navArgument("Id") { type = NavType.LongType })){ + EditCategoryScreen(navController, + it.arguments?.getLong("Id") + ) + } } } \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/OfflineTransactionRepository.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/OfflineTransactionRepository.kt index 402b2e9..d1be722 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/OfflineTransactionRepository.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/OfflineTransactionRepository.kt @@ -8,7 +8,7 @@ class OfflineTransactionRepository(private val transactionDao: TransactionDao): TransactionRepository { override fun getAllTransactionsStream(): Flow<List<Transaction>> = transactionDao.getAllTransactions() override fun getTransactionForIdStream(id: Long): Flow<Transaction?> = transactionDao.getTransactionForId(id) - override fun getCategoryWithTransactionForIdStream(id: Long): Flow<CategoryWithTransaction> = transactionDao.getCategoryWithTransactionForId(id) + override fun getTransactionWithCategoryForIdStream(id: Long): Flow<TransactionWithCategory> = transactionDao.getCategoryWithTransactionForId(id) override fun getCategoryWithTransactions(): Flow<List<CategoryWithTransaction>> = transactionDao.getCategoryWithTransaction() diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDao.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDao.kt index ae82277..3755614 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDao.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDao.kt @@ -7,6 +7,7 @@ import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Update import cz.cvut.fel.pda.financemanagement.model.CategoryWithTransaction +import cz.cvut.fel.pda.financemanagement.model.TransactionWithCategory import kotlinx.coroutines.flow.Flow @Dao @@ -23,7 +24,7 @@ interface TransactionDao { JOIN `transaction` ON `transaction`.categoryId = `category`.id WHERE `transaction`.id = :id """) - fun getCategoryWithTransactionForId(id: Long): Flow<CategoryWithTransaction> + fun getCategoryWithTransactionForId(id: Long): Flow<TransactionWithCategory> @Query(""" SELECT * FROM `category` diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionRepository.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionRepository.kt index e900a4c..60d929f 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionRepository.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionRepository.kt @@ -8,17 +8,7 @@ import kotlinx.coroutines.flow.map interface TransactionRepository { fun getAllTransactionsStream(): Flow<List<Transaction>> fun getTransactionForIdStream(id: Long): Flow<Transaction?> - fun getTransactionWithCategoryForIdStream(id: Long): Flow<TransactionWithCategory?> { - return getCategoryWithTransactionForIdStream(id).map { transaction -> - transaction.let { - TransactionWithCategory( - transaction = it.transaction.firstOrNull { transaction -> transaction.id == id } ?: Transaction(categoryId = 0L), - category = it.category - ) - } - } - } - fun getCategoryWithTransactionForIdStream(id: Long): Flow<CategoryWithTransaction> + fun getTransactionWithCategoryForIdStream(id: Long): Flow<TransactionWithCategory> fun getCategoryWithTransactions(): Flow<List<CategoryWithTransaction>> fun getTransactionsWithCategory(): Flow<List<TransactionWithCategory>> { diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/TransactionWithCategory.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/TransactionWithCategory.kt index 0e4a422..661daa9 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/TransactionWithCategory.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/TransactionWithCategory.kt @@ -1,9 +1,15 @@ package cz.cvut.fel.pda.financemanagement.model +import androidx.room.Embedded +import androidx.room.Relation import cz.cvut.fel.pda.financemanagement.database.category.Category import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction data class TransactionWithCategory( - val transaction: Transaction, + @Embedded val transaction: Transaction, + @Relation( + parentColumn = "categoryId", + entityColumn = "id" + ) val category: Category, ) \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/SideSheet.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/SideSheet.kt new file mode 100644 index 0000000..8c553b1 --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/SideSheet.kt @@ -0,0 +1,14 @@ +package cz.cvut.fel.pda.financemanagement.ui.components + +import androidx.compose.foundation.layout.Column +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable + +@Composable +fun SideSheet() { + Column { + Text("Preferences") + Text("Notifications") + Text("Documentation") + } +} \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/TopBar.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/TopBar.kt index 4472df4..05a9f80 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/TopBar.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/TopBar.kt @@ -4,18 +4,24 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Menu +import androidx.compose.material3.DrawerValue import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.ModalNavigationDrawer import androidx.compose.material3.Text +import androidx.compose.material3.rememberDrawerState import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.sp +import androidx.navigation.NavHostController +import kotlinx.coroutines.launch @Composable -fun TopBar() { +fun TopBar(navHostController: NavHostController) { Box(modifier = Modifier.fillMaxWidth()) { IconButton(onClick = { /* Handle navigation drawer opening here */ }) { Icon(Icons.Filled.Menu, contentDescription = "Menu") diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/AccountsScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/AccountsScreen.kt index f9b9d5c..e50e0dd 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/AccountsScreen.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/AccountsScreen.kt @@ -15,7 +15,7 @@ import cz.cvut.fel.pda.financemanagement.ui.components.TopBar fun AccountsScreen(navController: NavHostController){ Scaffold( topBar = { - TopBar() + TopBar(navController) }, bottomBar = { BottomNavigation(navController = navController) diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/LoansScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/LoansScreen.kt index 505b26c..926f5f9 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/LoansScreen.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/LoansScreen.kt @@ -15,7 +15,7 @@ import cz.cvut.fel.pda.financemanagement.ui.components.TopBar fun LoansScreen(navController: NavHostController){ Scaffold( topBar = { - TopBar() + TopBar(navController) }, bottomBar = { BottomNavigation(navController = navController) diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoriesScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoriesScreen.kt index fc7a6c1..7be1f9c 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoriesScreen.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoriesScreen.kt @@ -30,6 +30,7 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import cz.cvut.fel.pda.financemanagement.FinanceManagementScreens import java.util.Locale @Composable @@ -43,7 +44,7 @@ fun CategoriesScreen( Scaffold( topBar = { - TopBar() + TopBar(navController) }, content = { innerPadding -> @@ -62,10 +63,9 @@ fun CategoriesScreen( } }, floatingActionButton = { - FloatingActionButton(onClick = { }) { + FloatingActionButton(onClick = {navController.navigate("${FinanceManagementScreens.EditCategory.name}/0 ")}) { Icon(Icons.Default.Add, contentDescription = "Add") } - }, bottomBar = { BottomNavigation(navController = navController) diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoryDetailViewModel.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoryDetailViewModel.kt new file mode 100644 index 0000000..1aa0979 --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoryDetailViewModel.kt @@ -0,0 +1,66 @@ +package cz.cvut.fel.pda.financemanagement.ui.screens.cateegory + +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import cz.cvut.fel.pda.financemanagement.database.category.Category +import cz.cvut.fel.pda.financemanagement.database.category.CategoryName +import cz.cvut.fel.pda.financemanagement.database.category.CategoryType +import cz.cvut.fel.pda.financemanagement.database.category.CategoryRepository +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch +class CategoryDetailViewModel( + private val categoryRepository: CategoryRepository, + savedStateHandle: SavedStateHandle +): ViewModel() { + private var categoryId: Long = savedStateHandle.get<Long>("Id")!! + + val uiState: StateFlow<CategoryDetailUiState> = + categoryRepository + .getCategoryForIdStream(categoryId) + .map { + if (it != null) { + CategoryDetailUiState.Success(it) + } else if (categoryId == 0L) { + CategoryDetailUiState.Success( + Category( + id = 0, + name = CategoryName.GROCERIES, + type = CategoryType.EXPENSE + ) + ) + } else { + CategoryDetailUiState.Error(Exception("Category not found")) + } + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000L), + initialValue = CategoryDetailUiState.Loading + ) + fun setCategoryId(id: Long) { + categoryId = id + } + fun saveCategory( + type: CategoryType, + name: CategoryName, + ) { + viewModelScope.launch { + categoryRepository.insertCategories( + Category( + id = categoryId, + type = type, + name = name + ) + ) + } + } +} + +sealed interface CategoryDetailUiState { + data object Loading : CategoryDetailUiState + data class Success(val category: Category) : CategoryDetailUiState + data class Error(val exception: Exception) : CategoryDetailUiState +} \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/EditCategoryScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/EditCategoryScreen.kt new file mode 100644 index 0000000..244583d --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/EditCategoryScreen.kt @@ -0,0 +1,52 @@ +package cz.cvut.fel.pda.financemanagement.ui.screens.cateegory + +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavHostController +import cz.cvut.fel.pda.financemanagement.database.category.Category +import cz.cvut.fel.pda.financemanagement.ui.components.ShowErrorScreen +import cz.cvut.fel.pda.financemanagement.ui.components.ShowLoadingScreen +import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.ShowSuccessScreen +import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.TransactionDetailViewModel +import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.TransactionWithCategoryUiState +import cz.cvut.fel.pda.financemanagement.ui.utils.AppViewModelProvider + +@Composable +fun EditCategoryScreen(navController: NavHostController, + categoryId: Long?, + categoryDetailViewModel: CategoryDetailViewModel = viewModel( + factory = AppViewModelProvider.Factory + ),){ + val uiState by categoryDetailViewModel.uiState.collectAsStateWithLifecycle() + + when (val state = uiState) { + is CategoryDetailUiState.Error -> { + ShowErrorScreen( + errorMessage = state.exception.message ?: "Error", + onClick = { navController.popBackStack() } + ) + } + CategoryDetailUiState.Loading -> { + ShowLoadingScreen() + } + is CategoryDetailUiState.Success -> { + ShowCategorySuccessScreen( + state.category, + navController, + categoryDetailViewModel + ) + } + } +} + +@Composable +fun ShowCategorySuccessScreen( + category: Category, + navController: NavHostController, + categoryDetailViewModel: CategoryDetailViewModel +) { + Text(text="Success Screen") +} diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailScreen.kt index 2ed972e..9cb3fa9 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailScreen.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailScreen.kt @@ -38,7 +38,7 @@ fun TransactionDetailScreen( val transactionUiState: TransactionWithCategoryUiState by transactionViewModel.uiState.collectAsStateWithLifecycle() Scaffold ( - topBar = {TopBar()}, + topBar = {TopBar(navController)}, content = { innerPadding -> when (transactionUiState) { diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt index eb2de35..921f714 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt @@ -68,7 +68,7 @@ fun TransactionsScreen( ) { val transactionsUiState: TransactionsWithCategoryUiState by transactionViewModel.transactionsWithCategory.collectAsStateWithLifecycle() Scaffold( - topBar = { TopBar() }, + topBar = { TopBar(navController) }, content = { innerPadding -> when (transactionsUiState) { is TransactionsWithCategoryUiState.Loading -> { diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/utils/AppViewModelProvider.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/utils/AppViewModelProvider.kt index f854683..9480208 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/utils/AppViewModelProvider.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/utils/AppViewModelProvider.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.viewmodel.CreationExtras import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory import cz.cvut.fel.pda.financemanagement.FinanceManagementApplication +import cz.cvut.fel.pda.financemanagement.ui.screens.cateegory.CategoryDetailViewModel import cz.cvut.fel.pda.financemanagement.ui.screens.cateegory.CategoryViewModel import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.TransactionDetailViewModel import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.TransactionViewModel @@ -28,6 +29,12 @@ object AppViewModelProvider { financemanagementApplication().container.categoryRepository, ) } + initializer { + CategoryDetailViewModel( + financemanagementApplication().container.categoryRepository, + this.createSavedStateHandle() + ) + } } } -- GitLab From 24ffd1d64ad2ce6ee4c8e6f070d4e044281f9247 Mon Sep 17 00:00:00 2001 From: dovicrad <dovicrad@fel.cvut.cz> Date: Mon, 29 Apr 2024 18:13:50 +0200 Subject: [PATCH 08/12] :cooking: add transaction editing --- .../transaction/OfflineTransactionRepository.kt | 4 ++-- .../database/transaction/TransactionDao.kt | 4 ++-- .../database/transaction/TransactionRepository.kt | 4 ++-- ...thTransaction.kt => CategoryWithTransactions.kt} | 2 +- .../ui/screens/cateegory/CategoriesScreen.kt | 2 +- .../screens/transactions/TransactionDetailScreen.kt | 13 +++++++++++++ 6 files changed, 21 insertions(+), 8 deletions(-) rename app/src/main/java/cz/cvut/fel/pda/financemanagement/model/{CategoryWithTransaction.kt => CategoryWithTransactions.kt} (91%) diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/OfflineTransactionRepository.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/OfflineTransactionRepository.kt index d1be722..e0bc608 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/OfflineTransactionRepository.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/OfflineTransactionRepository.kt @@ -1,6 +1,6 @@ package cz.cvut.fel.pda.financemanagement.database.transaction -import cz.cvut.fel.pda.financemanagement.model.CategoryWithTransaction +import cz.cvut.fel.pda.financemanagement.model.CategoryWithTransactions import cz.cvut.fel.pda.financemanagement.model.TransactionWithCategory import kotlinx.coroutines.flow.Flow @@ -10,7 +10,7 @@ class OfflineTransactionRepository(private val transactionDao: TransactionDao): override fun getTransactionForIdStream(id: Long): Flow<Transaction?> = transactionDao.getTransactionForId(id) override fun getTransactionWithCategoryForIdStream(id: Long): Flow<TransactionWithCategory> = transactionDao.getCategoryWithTransactionForId(id) - override fun getCategoryWithTransactions(): Flow<List<CategoryWithTransaction>> = transactionDao.getCategoryWithTransaction() + override fun getCategoryWithTransactions(): Flow<List<CategoryWithTransactions>> = transactionDao.getCategoryWithTransaction() override suspend fun insertTransactions(vararg transactions: Transaction) = transactionDao.insertTransactions(*transactions) override suspend fun deleteTransaction(transaction: Transaction) = transactionDao.delete(transaction) diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDao.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDao.kt index 3755614..2abaedb 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDao.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDao.kt @@ -6,7 +6,7 @@ import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Update -import cz.cvut.fel.pda.financemanagement.model.CategoryWithTransaction +import cz.cvut.fel.pda.financemanagement.model.CategoryWithTransactions import cz.cvut.fel.pda.financemanagement.model.TransactionWithCategory import kotlinx.coroutines.flow.Flow @@ -31,7 +31,7 @@ interface TransactionDao { INNER JOIN `transaction` ON `transaction`.categoryId = `category`.id ORDER BY `category`.id ASC """) - fun getCategoryWithTransaction(): Flow<List<CategoryWithTransaction>> + fun getCategoryWithTransaction(): Flow<List<CategoryWithTransactions>> @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertTransactions(vararg transactions: Transaction) diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionRepository.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionRepository.kt index 60d929f..6dd9d71 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionRepository.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionRepository.kt @@ -1,6 +1,6 @@ package cz.cvut.fel.pda.financemanagement.database.transaction -import cz.cvut.fel.pda.financemanagement.model.CategoryWithTransaction +import cz.cvut.fel.pda.financemanagement.model.CategoryWithTransactions import cz.cvut.fel.pda.financemanagement.model.TransactionWithCategory import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map @@ -10,7 +10,7 @@ interface TransactionRepository { fun getTransactionForIdStream(id: Long): Flow<Transaction?> fun getTransactionWithCategoryForIdStream(id: Long): Flow<TransactionWithCategory> - fun getCategoryWithTransactions(): Flow<List<CategoryWithTransaction>> + fun getCategoryWithTransactions(): Flow<List<CategoryWithTransactions>> fun getTransactionsWithCategory(): Flow<List<TransactionWithCategory>> { return getCategoryWithTransactions().map { categoryWithTransactionList -> categoryWithTransactionList.flatMap { categoryWithTransaction -> diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/CategoryWithTransaction.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/CategoryWithTransactions.kt similarity index 91% rename from app/src/main/java/cz/cvut/fel/pda/financemanagement/model/CategoryWithTransaction.kt rename to app/src/main/java/cz/cvut/fel/pda/financemanagement/model/CategoryWithTransactions.kt index 5d11a1e..727fb9c 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/CategoryWithTransaction.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/CategoryWithTransactions.kt @@ -5,7 +5,7 @@ import androidx.room.Relation import cz.cvut.fel.pda.financemanagement.database.category.Category import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction -data class CategoryWithTransaction( +data class CategoryWithTransactions( @Embedded val category: Category, @Relation( parentColumn = "id", diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoriesScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoriesScreen.kt index 7be1f9c..9aa873b 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoriesScreen.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoriesScreen.kt @@ -63,7 +63,7 @@ fun CategoriesScreen( } }, floatingActionButton = { - FloatingActionButton(onClick = {navController.navigate("${FinanceManagementScreens.EditCategory.name}/0 ")}) { + FloatingActionButton(onClick = {navController.navigate("${FinanceManagementScreens.EditCategory.name}/0")}) { Icon(Icons.Default.Add, contentDescription = "Add") } }, diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailScreen.kt index 9cb3fa9..063175e 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailScreen.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailScreen.kt @@ -7,6 +7,10 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Edit +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -17,6 +21,8 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.CreationExtras import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController +import cz.cvut.fel.pda.financemanagement.FinanceManagementApplication +import cz.cvut.fel.pda.financemanagement.FinanceManagementScreens import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction import cz.cvut.fel.pda.financemanagement.model.TransactionWithCategory import cz.cvut.fel.pda.financemanagement.ui.components.BottomNavigation @@ -55,6 +61,13 @@ fun TransactionDetailScreen( } } }, + floatingActionButton = { + FloatingActionButton(onClick = { + navController.navigate("${FinanceManagementScreens.EditTransaction.name}/${transactionId}") + }) { + Icon(Icons.Default.Edit, contentDescription = "Edit Transaction") + } + }, bottomBar = { BottomNavigation(navController = navController) } -- GitLab From faeaea29314bf51ba125cd5d3b4e18355c692c7a Mon Sep 17 00:00:00 2001 From: dovicrad <dovicrad@fel.cvut.cz> Date: Mon, 29 Apr 2024 18:13:50 +0200 Subject: [PATCH 09/12] :cooking: add category type selection --- app/src/main/AndroidManifest.xml | 2 + .../financemanagement/FinanceManagementApp.kt | 14 +- .../financemanagement/database/AppDatabase.kt | 4 +- .../database/category/Category.kt | 6 +- .../{transaction => operation}/Operation.kt | 10 +- .../OfflineTransactionRepository.kt | 6 +- .../transaction/Transaction.kt | 3 +- .../transaction/TransactionDao.kt | 6 +- .../transaction/TransactionRepository.kt | 6 +- .../pda/financemanagement/di/AppContainer.kt | 4 +- ...saction.kt => CategoryWithTransactions.kt} | 4 +- .../model/TransactionWithCategory.kt | 2 +- .../ui/components/BottomNavigation.kt | 7 +- .../financemanagement/ui/components/TopBar.kt | 5 - .../screens/cateegory/EditCategoryScreen.kt | 52 ----- .../CategoriesScreen.kt | 32 +-- .../screens/category/CategoryDetailScreen.kt | 91 ++++++++ .../CategoryDetailViewModel.kt | 10 +- .../CategoryViewModel.kt | 2 +- .../ui/screens/category/EditCategoryScreen.kt | 195 ++++++++++++++++++ .../transactions/EditTransactionScreen.kt | 1 - .../transactions/TransactionDetailScreen.kt | 15 +- .../TransactionDetailViewModel.kt | 7 +- .../transactions/TransactionViewModel.kt | 2 +- .../transactions/TransactionsScreen.kt | 2 +- .../ui/utils/AppViewModelProvider.kt | 4 +- 26 files changed, 366 insertions(+), 126 deletions(-) rename app/src/main/java/cz/cvut/fel/pda/financemanagement/database/{transaction => operation}/Operation.kt (62%) rename app/src/main/java/cz/cvut/fel/pda/financemanagement/database/{ => operation}/transaction/OfflineTransactionRepository.kt (88%) rename app/src/main/java/cz/cvut/fel/pda/financemanagement/database/{ => operation}/transaction/Transaction.kt (77%) rename app/src/main/java/cz/cvut/fel/pda/financemanagement/database/{ => operation}/transaction/TransactionDao.kt (93%) rename app/src/main/java/cz/cvut/fel/pda/financemanagement/database/{ => operation}/transaction/TransactionRepository.kt (93%) rename app/src/main/java/cz/cvut/fel/pda/financemanagement/model/{CategoryWithTransaction.kt => CategoryWithTransactions.kt} (73%) delete mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/EditCategoryScreen.kt rename app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/{cateegory => category}/CategoriesScreen.kt (84%) create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/CategoryDetailScreen.kt rename app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/{cateegory => category}/CategoryDetailViewModel.kt (90%) rename app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/{cateegory => category}/CategoryViewModel.kt (96%) create mode 100644 app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/EditCategoryScreen.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c49185b..b118bc2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"> + <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/> + <application android:name=".FinanceManagementApplication" android:allowBackup="true" diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApp.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApp.kt index d7c98c5..6e2583a 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApp.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/FinanceManagementApp.kt @@ -9,20 +9,22 @@ import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument import cz.cvut.fel.pda.financemanagement.ui.screens.AccountsScreen -import cz.cvut.fel.pda.financemanagement.ui.screens.cateegory.CategoriesScreen import cz.cvut.fel.pda.financemanagement.ui.screens.LoansScreen -import cz.cvut.fel.pda.financemanagement.ui.screens.cateegory.EditCategoryScreen +import cz.cvut.fel.pda.financemanagement.ui.screens.category.CategoriesScreen +import cz.cvut.fel.pda.financemanagement.ui.screens.category.CategoryDetailScreen +import cz.cvut.fel.pda.financemanagement.ui.screens.category.EditCategoryScreen import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.EditTransactionScreen import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.TransactionDetailScreen import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.TransactionsScreen -enum class FinanceManagementScreens() { +enum class FinanceManagementScreens { Transactions, TransactionsDetail, EditTransaction, Categories, + CategoryDetail, EditCategory, Accounts, @@ -69,5 +71,11 @@ fun FinanceManagementApp() { it.arguments?.getLong("Id") ) } + composable(route = "${FinanceManagementScreens.CategoryDetail.name}/{Id}", + arguments = listOf(navArgument("Id") { type = NavType.LongType })){ + CategoryDetailScreen(navController = navController, + categoryId = it.arguments?.getLong("Id") + ) + } } } \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/AppDatabase.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/AppDatabase.kt index ac315bf..0e14141 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/AppDatabase.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/AppDatabase.kt @@ -7,8 +7,8 @@ import androidx.room.RoomDatabase import androidx.room.TypeConverters import cz.cvut.fel.pda.financemanagement.database.category.Category import cz.cvut.fel.pda.financemanagement.database.category.CategoryDao -import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction -import cz.cvut.fel.pda.financemanagement.database.transaction.TransactionDao +import cz.cvut.fel.pda.financemanagement.database.operation.transaction.Transaction +import cz.cvut.fel.pda.financemanagement.database.operation.transaction.TransactionDao import cz.cvut.fel.pda.financemanagement.database.typeConvertion.Converters @Database(entities = [Transaction::class, Category::class], version = 7, exportSchema = false) diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/category/Category.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/category/Category.kt index 9cc2b60..dc29cba 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/category/Category.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/category/Category.kt @@ -7,7 +7,7 @@ import androidx.room.PrimaryKey data class Category( @PrimaryKey(autoGenerate = true) val id: Long = 0, - val name: CategoryName, + val name: String, val type: CategoryType, ) @@ -15,7 +15,3 @@ enum class CategoryType { EXPENSE, INCOME, } - -enum class CategoryName { - GROCERIES, -} diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/Operation.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/Operation.kt similarity index 62% rename from app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/Operation.kt rename to app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/Operation.kt index 5618432..4b22e67 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/Operation.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/Operation.kt @@ -1,11 +1,9 @@ -package cz.cvut.fel.pda.financemanagement.database.transaction; +package cz.cvut.fel.pda.financemanagement.database.operation -import androidx.room.Entity; -import androidx.room.PrimaryKey; - -import java.math.BigDecimal; +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.math.BigDecimal import java.time.LocalDateTime -import java.util.Date @Entity(tableName = "operation") abstract class Operation { diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/OfflineTransactionRepository.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/OfflineTransactionRepository.kt similarity index 88% rename from app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/OfflineTransactionRepository.kt rename to app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/OfflineTransactionRepository.kt index d1be722..f27dd68 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/OfflineTransactionRepository.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/OfflineTransactionRepository.kt @@ -1,6 +1,6 @@ -package cz.cvut.fel.pda.financemanagement.database.transaction +package cz.cvut.fel.pda.financemanagement.database.operation.transaction -import cz.cvut.fel.pda.financemanagement.model.CategoryWithTransaction +import cz.cvut.fel.pda.financemanagement.model.CategoryWithTransactions import cz.cvut.fel.pda.financemanagement.model.TransactionWithCategory import kotlinx.coroutines.flow.Flow @@ -10,7 +10,7 @@ class OfflineTransactionRepository(private val transactionDao: TransactionDao): override fun getTransactionForIdStream(id: Long): Flow<Transaction?> = transactionDao.getTransactionForId(id) override fun getTransactionWithCategoryForIdStream(id: Long): Flow<TransactionWithCategory> = transactionDao.getCategoryWithTransactionForId(id) - override fun getCategoryWithTransactions(): Flow<List<CategoryWithTransaction>> = transactionDao.getCategoryWithTransaction() + override fun getCategoryWithTransactions(): Flow<List<CategoryWithTransactions>> = transactionDao.getCategoryWithTransaction() override suspend fun insertTransactions(vararg transactions: Transaction) = transactionDao.insertTransactions(*transactions) override suspend fun deleteTransaction(transaction: Transaction) = transactionDao.delete(transaction) diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/Transaction.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/Transaction.kt similarity index 77% rename from app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/Transaction.kt rename to app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/Transaction.kt index 5042f03..750c837 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/Transaction.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/Transaction.kt @@ -1,7 +1,8 @@ -package cz.cvut.fel.pda.financemanagement.database.transaction +package cz.cvut.fel.pda.financemanagement.database.operation.transaction import androidx.room.Entity import androidx.room.PrimaryKey +import cz.cvut.fel.pda.financemanagement.database.operation.Operation import java.math.BigDecimal import java.time.LocalDateTime diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDao.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/TransactionDao.kt similarity index 93% rename from app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDao.kt rename to app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/TransactionDao.kt index 3755614..bd0d257 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionDao.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/TransactionDao.kt @@ -1,4 +1,4 @@ -package cz.cvut.fel.pda.financemanagement.database.transaction +package cz.cvut.fel.pda.financemanagement.database.operation.transaction import androidx.room.Dao import androidx.room.Delete @@ -6,7 +6,7 @@ import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Update -import cz.cvut.fel.pda.financemanagement.model.CategoryWithTransaction +import cz.cvut.fel.pda.financemanagement.model.CategoryWithTransactions import cz.cvut.fel.pda.financemanagement.model.TransactionWithCategory import kotlinx.coroutines.flow.Flow @@ -31,7 +31,7 @@ interface TransactionDao { INNER JOIN `transaction` ON `transaction`.categoryId = `category`.id ORDER BY `category`.id ASC """) - fun getCategoryWithTransaction(): Flow<List<CategoryWithTransaction>> + fun getCategoryWithTransaction(): Flow<List<CategoryWithTransactions>> @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertTransactions(vararg transactions: Transaction) diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionRepository.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/TransactionRepository.kt similarity index 93% rename from app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionRepository.kt rename to app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/TransactionRepository.kt index 60d929f..69e0220 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/transaction/TransactionRepository.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/TransactionRepository.kt @@ -1,6 +1,6 @@ -package cz.cvut.fel.pda.financemanagement.database.transaction +package cz.cvut.fel.pda.financemanagement.database.operation.transaction -import cz.cvut.fel.pda.financemanagement.model.CategoryWithTransaction +import cz.cvut.fel.pda.financemanagement.model.CategoryWithTransactions import cz.cvut.fel.pda.financemanagement.model.TransactionWithCategory import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map @@ -10,7 +10,7 @@ interface TransactionRepository { fun getTransactionForIdStream(id: Long): Flow<Transaction?> fun getTransactionWithCategoryForIdStream(id: Long): Flow<TransactionWithCategory> - fun getCategoryWithTransactions(): Flow<List<CategoryWithTransaction>> + fun getCategoryWithTransactions(): Flow<List<CategoryWithTransactions>> fun getTransactionsWithCategory(): Flow<List<TransactionWithCategory>> { return getCategoryWithTransactions().map { categoryWithTransactionList -> categoryWithTransactionList.flatMap { categoryWithTransaction -> diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/di/AppContainer.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/di/AppContainer.kt index 7ee3744..035c1c0 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/di/AppContainer.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/di/AppContainer.kt @@ -4,8 +4,8 @@ import android.content.Context import cz.cvut.fel.pda.financemanagement.database.AppDatabase import cz.cvut.fel.pda.financemanagement.database.category.CategoryRepository import cz.cvut.fel.pda.financemanagement.database.category.OfflineCategoryRepository -import cz.cvut.fel.pda.financemanagement.database.transaction.OfflineTransactionRepository -import cz.cvut.fel.pda.financemanagement.database.transaction.TransactionRepository +import cz.cvut.fel.pda.financemanagement.database.operation.transaction.OfflineTransactionRepository +import cz.cvut.fel.pda.financemanagement.database.operation.transaction.TransactionRepository interface AppContainer { val transactionRepository: TransactionRepository diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/CategoryWithTransaction.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/CategoryWithTransactions.kt similarity index 73% rename from app/src/main/java/cz/cvut/fel/pda/financemanagement/model/CategoryWithTransaction.kt rename to app/src/main/java/cz/cvut/fel/pda/financemanagement/model/CategoryWithTransactions.kt index 5d11a1e..81117af 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/CategoryWithTransaction.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/CategoryWithTransactions.kt @@ -3,9 +3,9 @@ package cz.cvut.fel.pda.financemanagement.model import androidx.room.Embedded import androidx.room.Relation import cz.cvut.fel.pda.financemanagement.database.category.Category -import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction +import cz.cvut.fel.pda.financemanagement.database.operation.transaction.Transaction -data class CategoryWithTransaction( +data class CategoryWithTransactions( @Embedded val category: Category, @Relation( parentColumn = "id", diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/TransactionWithCategory.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/TransactionWithCategory.kt index 661daa9..b24f6e5 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/TransactionWithCategory.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/model/TransactionWithCategory.kt @@ -3,7 +3,7 @@ package cz.cvut.fel.pda.financemanagement.model import androidx.room.Embedded import androidx.room.Relation import cz.cvut.fel.pda.financemanagement.database.category.Category -import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction +import cz.cvut.fel.pda.financemanagement.database.operation.transaction.Transaction data class TransactionWithCategory( @Embedded val transaction: Transaction, diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/BottomNavigation.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/BottomNavigation.kt index ef012a3..19e6087 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/BottomNavigation.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/BottomNavigation.kt @@ -34,12 +34,10 @@ fun BottomNavigation(navController: NavHostController) { selected = currentRoute == it.second, onClick = { navController.navigate(it.second) { - // Avoid multiple copies of the same destination when re-selecting the same item launchSingleTop = true popUpTo(navController.graph.findStartDestination().id) { - saveState = true // Save the state of all destinations before popping them + saveState = true } - // Restore state when navigating to a previously selected item restoreState = true } } @@ -50,8 +48,7 @@ fun BottomNavigation(navController: NavHostController) { @Composable fun GreyDot() { - Box( - modifier = Modifier + Box(modifier = Modifier .size(16.dp) .background(Color.DarkGray, CircleShape) ) diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/TopBar.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/TopBar.kt index 05a9f80..3c673a3 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/TopBar.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/components/TopBar.kt @@ -4,21 +4,16 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Menu -import androidx.compose.material3.DrawerValue import androidx.compose.material3.Icon import androidx.compose.material3.IconButton -import androidx.compose.material3.ModalNavigationDrawer import androidx.compose.material3.Text -import androidx.compose.material3.rememberDrawerState import androidx.compose.runtime.Composable -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.sp import androidx.navigation.NavHostController -import kotlinx.coroutines.launch @Composable fun TopBar(navHostController: NavHostController) { diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/EditCategoryScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/EditCategoryScreen.kt deleted file mode 100644 index 244583d..0000000 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/EditCategoryScreen.kt +++ /dev/null @@ -1,52 +0,0 @@ -package cz.cvut.fel.pda.financemanagement.ui.screens.cateegory - -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.lifecycle.viewmodel.compose.viewModel -import androidx.navigation.NavHostController -import cz.cvut.fel.pda.financemanagement.database.category.Category -import cz.cvut.fel.pda.financemanagement.ui.components.ShowErrorScreen -import cz.cvut.fel.pda.financemanagement.ui.components.ShowLoadingScreen -import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.ShowSuccessScreen -import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.TransactionDetailViewModel -import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.TransactionWithCategoryUiState -import cz.cvut.fel.pda.financemanagement.ui.utils.AppViewModelProvider - -@Composable -fun EditCategoryScreen(navController: NavHostController, - categoryId: Long?, - categoryDetailViewModel: CategoryDetailViewModel = viewModel( - factory = AppViewModelProvider.Factory - ),){ - val uiState by categoryDetailViewModel.uiState.collectAsStateWithLifecycle() - - when (val state = uiState) { - is CategoryDetailUiState.Error -> { - ShowErrorScreen( - errorMessage = state.exception.message ?: "Error", - onClick = { navController.popBackStack() } - ) - } - CategoryDetailUiState.Loading -> { - ShowLoadingScreen() - } - is CategoryDetailUiState.Success -> { - ShowCategorySuccessScreen( - state.category, - navController, - categoryDetailViewModel - ) - } - } -} - -@Composable -fun ShowCategorySuccessScreen( - category: Category, - navController: NavHostController, - categoryDetailViewModel: CategoryDetailViewModel -) { - Text(text="Success Screen") -} diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoriesScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/CategoriesScreen.kt similarity index 84% rename from app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoriesScreen.kt rename to app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/CategoriesScreen.kt index 7be1f9c..6f27a75 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoriesScreen.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/CategoriesScreen.kt @@ -1,22 +1,31 @@ -package cz.cvut.fel.pda.financemanagement.ui.screens.cateegory +package cz.cvut.fel.pda.financemanagement.ui.screens.category +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +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.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController +import cz.cvut.fel.pda.financemanagement.FinanceManagementScreens import cz.cvut.fel.pda.financemanagement.database.category.Category import cz.cvut.fel.pda.financemanagement.database.category.CategoryType import cz.cvut.fel.pda.financemanagement.ui.components.BottomNavigation @@ -24,13 +33,6 @@ import cz.cvut.fel.pda.financemanagement.ui.components.ShowErrorScreen import cz.cvut.fel.pda.financemanagement.ui.components.ShowLoadingScreen import cz.cvut.fel.pda.financemanagement.ui.components.TopBar import cz.cvut.fel.pda.financemanagement.ui.utils.AppViewModelProvider -import androidx.compose.foundation.lazy.items -import androidx.compose.material3.HorizontalDivider -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import cz.cvut.fel.pda.financemanagement.FinanceManagementScreens import java.util.Locale @Composable @@ -63,7 +65,7 @@ fun CategoriesScreen( } }, floatingActionButton = { - FloatingActionButton(onClick = {navController.navigate("${FinanceManagementScreens.EditCategory.name}/0 ")}) { + FloatingActionButton(onClick = {navController.navigate("${FinanceManagementScreens.EditCategory.name}/0")}) { Icon(Icons.Default.Add, contentDescription = "Add") } }, @@ -81,25 +83,25 @@ fun CategoriesScreenContent(categories: List<Category>, navController: NavHostCo Column(modifier = Modifier.padding(innerPadding).padding(horizontal = 14.dp)) { Text(text = "Expense Categories:", style = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Bold)) HorizontalDivider() - CategoriesList(expenseCategories) + CategoriesList(expenseCategories, navController) Text(text = "Income Categories:", style = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Bold)) HorizontalDivider() - CategoriesList(incomeCategories) + CategoriesList(incomeCategories, navController) } } @Composable -fun CategoriesList(categories: List<Category>) { +fun CategoriesList(categories: List<Category>, navController: NavHostController) { LazyColumn { items(categories) { category -> - CategoryItem(category) + CategoryItem(category,navController) } } } @Composable -fun CategoryItem(category: Category) { - Row { +fun CategoryItem(category: Category, navController: NavHostController) { + Row(modifier = Modifier.padding(8.dp).clickable { navController.navigate("${FinanceManagementScreens.CategoryDetail}/${category.id}")}){ Text(text = category.name.toString().lowercase().replaceFirstChar { it.titlecase(Locale.ROOT)}) } } diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/CategoryDetailScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/CategoryDetailScreen.kt new file mode 100644 index 0000000..e182a49 --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/CategoryDetailScreen.kt @@ -0,0 +1,91 @@ +package cz.cvut.fel.pda.financemanagement.ui.screens.category + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Edit +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavHostController +import cz.cvut.fel.pda.financemanagement.FinanceManagementScreens +import cz.cvut.fel.pda.financemanagement.database.category.Category +import cz.cvut.fel.pda.financemanagement.ui.components.BottomNavigation +import cz.cvut.fel.pda.financemanagement.ui.components.ShowErrorScreen +import cz.cvut.fel.pda.financemanagement.ui.components.ShowLoadingScreen +import cz.cvut.fel.pda.financemanagement.ui.components.TopBar +import cz.cvut.fel.pda.financemanagement.ui.utils.AppViewModelProvider + + +@Composable +fun CategoryDetailScreen( + navController: NavHostController, + categoryId: Long?, + categoryViewModel: CategoryDetailViewModel = viewModel( + factory = AppViewModelProvider.Factory + ) +){ + categoryViewModel.setCategoryId(categoryId ?: 0L) + val categoryDetailUiState: CategoryDetailUiState by categoryViewModel.uiState.collectAsStateWithLifecycle() + + Scaffold ( + topBar = {TopBar(navController)}, + content = { + innerPadding -> + when (categoryDetailUiState) { + is CategoryDetailUiState.Loading -> { + ShowLoadingScreen() + } + is CategoryDetailUiState.Error -> { + ShowErrorScreen( + errorMessage = (categoryDetailUiState as CategoryDetailUiState.Error).exception.message ?: "Error", + ) + } + is CategoryDetailUiState.Success -> { + CategoryScreenContent( (categoryDetailUiState as CategoryDetailUiState.Success).category, innerPadding, navController) + } + } + }, + floatingActionButton = { + FloatingActionButton(onClick = { + navController.navigate("${FinanceManagementScreens.EditCategory.name}/${categoryId}") + }) { + Icon(Icons.Default.Edit, contentDescription = "Edit Category") + } + }, + bottomBar = { + BottomNavigation(navController = navController) + } + ) +} + +@Composable +fun CategoryScreenContent(category: Category, innerPadding: PaddingValues, navController: NavHostController) { + Column(modifier = Modifier.padding(innerPadding)) { + Text(text = "Category Details") + Spacer(modifier = Modifier.height(16.dp)) + Row { + Text(text = "ID: ") + Text(text = category.id.toString()) + } + Row { + Text(text = "Name: ") + Text(text = category.name.toString()) + } + Row { + Text(text = "Type: ") + Text(text = category.type.toString()) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoryDetailViewModel.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/CategoryDetailViewModel.kt similarity index 90% rename from app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoryDetailViewModel.kt rename to app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/CategoryDetailViewModel.kt index 1aa0979..92ef8e1 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoryDetailViewModel.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/CategoryDetailViewModel.kt @@ -1,17 +1,17 @@ -package cz.cvut.fel.pda.financemanagement.ui.screens.cateegory +package cz.cvut.fel.pda.financemanagement.ui.screens.category import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import cz.cvut.fel.pda.financemanagement.database.category.Category -import cz.cvut.fel.pda.financemanagement.database.category.CategoryName -import cz.cvut.fel.pda.financemanagement.database.category.CategoryType import cz.cvut.fel.pda.financemanagement.database.category.CategoryRepository +import cz.cvut.fel.pda.financemanagement.database.category.CategoryType import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch + class CategoryDetailViewModel( private val categoryRepository: CategoryRepository, savedStateHandle: SavedStateHandle @@ -28,7 +28,7 @@ class CategoryDetailViewModel( CategoryDetailUiState.Success( Category( id = 0, - name = CategoryName.GROCERIES, + name ="Groceries", type = CategoryType.EXPENSE ) ) @@ -45,7 +45,7 @@ class CategoryDetailViewModel( } fun saveCategory( type: CategoryType, - name: CategoryName, + name: String, ) { viewModelScope.launch { categoryRepository.insertCategories( diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoryViewModel.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/CategoryViewModel.kt similarity index 96% rename from app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoryViewModel.kt rename to app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/CategoryViewModel.kt index 4777dfb..0c639e7 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/cateegory/CategoryViewModel.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/CategoryViewModel.kt @@ -1,4 +1,4 @@ -package cz.cvut.fel.pda.financemanagement.ui.screens.cateegory +package cz.cvut.fel.pda.financemanagement.ui.screens.category import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/EditCategoryScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/EditCategoryScreen.kt new file mode 100644 index 0000000..2fa203d --- /dev/null +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/EditCategoryScreen.kt @@ -0,0 +1,195 @@ +package cz.cvut.fel.pda.financemanagement.ui.screens.category + +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Close +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SegmentedButton +import androidx.compose.material3.SegmentedButtonDefaults +import androidx.compose.material3.SingleChoiceSegmentedButtonRow +import androidx.compose.material3.Switch +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.text.toLowerCase +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavHostController +import cz.cvut.fel.pda.financemanagement.database.category.Category +import cz.cvut.fel.pda.financemanagement.database.category.CategoryType +import cz.cvut.fel.pda.financemanagement.ui.components.ShowErrorScreen +import cz.cvut.fel.pda.financemanagement.ui.components.ShowLoadingScreen +import cz.cvut.fel.pda.financemanagement.ui.utils.AppViewModelProvider +import java.math.BigDecimal +import java.util.Locale + +@Composable +fun EditCategoryScreen(navController: NavHostController, + categoryId: Long?, + categoryDetailViewModel: CategoryDetailViewModel = viewModel( + factory = AppViewModelProvider.Factory + ),){ + val uiState by categoryDetailViewModel.uiState.collectAsStateWithLifecycle() + + when (val state = uiState) { + is CategoryDetailUiState.Error -> { + ShowErrorScreen( + errorMessage = state.exception.message ?: "Error", + onClick = { navController.popBackStack() } + ) + } + CategoryDetailUiState.Loading -> { + ShowLoadingScreen() + } + is CategoryDetailUiState.Success -> { + ShowCategorySuccessScreen( + state.category, + navController, + categoryDetailViewModel + ) + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ShowCategorySuccessScreen( + category: Category, + navController: NavHostController, + categoryDetailViewModel: CategoryDetailViewModel +) { + val type = rememberSaveable { mutableStateOf(category.type) } + val name = rememberSaveable { mutableStateOf(category.name) } + val options = listOf(CategoryType.INCOME, CategoryType.EXPENSE) + val selectedOption = remember { mutableStateOf(type.value) } + + + Scaffold( + topBar = { + TopAppBar( + title = { Text("Category edit") }, + colors = TopAppBarDefaults.mediumTopAppBarColors( + containerColor = MaterialTheme.colorScheme.primaryContainer + ), + modifier = Modifier, + navigationIcon = { + IconButton(onClick = { navController.popBackStack() }) { + Icon( + imageVector = Icons.Outlined.Close, + contentDescription = "cs" + ) + } + }, + actions = { + TextButton( + onClick = { + categoryDetailViewModel.saveCategory( + type = type.value, + name = name.value + ) + navController.popBackStack() + }, + modifier = Modifier.padding(16.dp) + ) { + Text(text = "save") + } + } + ) + }, + content = { + innerPadding -> + Column ( + modifier = Modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()) + .padding(innerPadding), // use innerPadding here + horizontalAlignment = Alignment.CenterHorizontally + ) { + SingleChoiceSegmentedButtonRow { + options.forEachIndexed{index, option -> + SegmentedButton( + selected = selectedOption.value == option, + onClick = { + selectedOption.value = option + type.value = option + }, + shape = SegmentedButtonDefaults.itemShape(index=index ,count = options.size) + ) { + Text(text = option.name.lowercase(Locale.getDefault())) + } + } + } + + val spaceModifier = Modifier + .fillMaxWidth() + .padding(24.dp, 4.dp) + OutlinedTextField( + value = name.value, // Use the correct variable + onValueChange = { newValue -> + name.value = newValue // Use the correct variable + }, + label = { Text("name") }, // Use the correct label + modifier = spaceModifier + ) + } + } + ) +} +@Composable +fun EditTransactionDetailSuccessContent( + innerPadding: PaddingValues, + type: MutableState<CategoryType>, // Specify the correct type + name: MutableState<String> +) { + val openDialog = remember { mutableStateOf(false) } + + Column ( + modifier = Modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()) + .padding(innerPadding), // use innerPadding here + horizontalAlignment = Alignment.CenterHorizontally + ) { + val spaceModifier = Modifier + .fillMaxWidth() + .padding(24.dp, 4.dp) + OutlinedTextField( + value = name.value, // Use the correct variable + onValueChange = { newValue -> + name.value = newValue // Use the correct variable + }, + label = { Text("name") }, // Use the correct label + modifier = spaceModifier + ) + // Add OutlinedTextField for date and dropdown menu for category here + } +} \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/EditTransactionScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/EditTransactionScreen.kt index 5ea69ae..a1a78ef 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/EditTransactionScreen.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/EditTransactionScreen.kt @@ -30,7 +30,6 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController -import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction import cz.cvut.fel.pda.financemanagement.model.TransactionWithCategory import cz.cvut.fel.pda.financemanagement.ui.components.ShowErrorScreen import cz.cvut.fel.pda.financemanagement.ui.components.ShowLoadingScreen diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailScreen.kt index 9cb3fa9..a4b4d04 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailScreen.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailScreen.kt @@ -1,12 +1,15 @@ package cz.cvut.fel.pda.financemanagement.ui.screens.transactions -import android.provider.Settings.Global.putLong import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Edit +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -14,10 +17,9 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.lifecycle.viewmodel.CreationExtras import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController -import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction +import cz.cvut.fel.pda.financemanagement.FinanceManagementScreens import cz.cvut.fel.pda.financemanagement.model.TransactionWithCategory import cz.cvut.fel.pda.financemanagement.ui.components.BottomNavigation import cz.cvut.fel.pda.financemanagement.ui.components.ShowErrorScreen @@ -55,6 +57,13 @@ fun TransactionDetailScreen( } } }, + floatingActionButton = { + FloatingActionButton(onClick = { + navController.navigate("${FinanceManagementScreens.EditTransaction.name}/${transactionId}") + }) { + Icon(Icons.Default.Edit, contentDescription = "Edit Transaction") + } + }, bottomBar = { BottomNavigation(navController = navController) } diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailViewModel.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailViewModel.kt index a9eceea..e3271b5 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailViewModel.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailViewModel.kt @@ -4,10 +4,9 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import cz.cvut.fel.pda.financemanagement.database.category.Category -import cz.cvut.fel.pda.financemanagement.database.category.CategoryName import cz.cvut.fel.pda.financemanagement.database.category.CategoryType -import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction -import cz.cvut.fel.pda.financemanagement.database.transaction.TransactionRepository +import cz.cvut.fel.pda.financemanagement.database.operation.transaction.Transaction +import cz.cvut.fel.pda.financemanagement.database.operation.transaction.TransactionRepository import cz.cvut.fel.pda.financemanagement.model.TransactionWithCategory import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -32,7 +31,7 @@ class TransactionDetailViewModel( TransactionWithCategoryUiState.Success( TransactionWithCategory( transaction = Transaction(categoryId = 0L), - category = Category(0, CategoryName.GROCERIES,CategoryType.INCOME) + category = Category(0, "Groceries",CategoryType.INCOME) ) ) } else { diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionViewModel.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionViewModel.kt index b90f770..546fa27 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionViewModel.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionViewModel.kt @@ -2,7 +2,7 @@ package cz.cvut.fel.pda.financemanagement.ui.screens.transactions import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import cz.cvut.fel.pda.financemanagement.database.transaction.TransactionRepository +import cz.cvut.fel.pda.financemanagement.database.operation.transaction.TransactionRepository import cz.cvut.fel.pda.financemanagement.model.TransactionWithCategory import cz.cvut.fel.pda.financemanagement.model.common.Result import cz.cvut.fel.pda.financemanagement.model.common.asResult diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt index 921f714..4f10d61 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt @@ -48,7 +48,7 @@ import androidx.navigation.compose.rememberNavController import cz.cvut.fel.pda.financemanagement.FinanceManagementScreens import cz.cvut.fel.pda.financemanagement.database.category.Category import cz.cvut.fel.pda.financemanagement.database.category.CategoryType -import cz.cvut.fel.pda.financemanagement.database.transaction.Transaction +import cz.cvut.fel.pda.financemanagement.database.operation.transaction.Transaction import cz.cvut.fel.pda.financemanagement.model.TransactionWithCategory import cz.cvut.fel.pda.financemanagement.ui.components.BottomNavigation import cz.cvut.fel.pda.financemanagement.ui.components.ShowErrorScreen diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/utils/AppViewModelProvider.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/utils/AppViewModelProvider.kt index 9480208..2c9e889 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/utils/AppViewModelProvider.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/utils/AppViewModelProvider.kt @@ -6,8 +6,8 @@ import androidx.lifecycle.viewmodel.CreationExtras import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory import cz.cvut.fel.pda.financemanagement.FinanceManagementApplication -import cz.cvut.fel.pda.financemanagement.ui.screens.cateegory.CategoryDetailViewModel -import cz.cvut.fel.pda.financemanagement.ui.screens.cateegory.CategoryViewModel +import cz.cvut.fel.pda.financemanagement.ui.screens.category.CategoryDetailViewModel +import cz.cvut.fel.pda.financemanagement.ui.screens.category.CategoryViewModel import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.TransactionDetailViewModel import cz.cvut.fel.pda.financemanagement.ui.screens.transactions.TransactionViewModel -- GitLab From d0174093aa514ca9e9c0c9d855e63b7594a2703c Mon Sep 17 00:00:00 2001 From: dovicrad <dovicrad@fel.cvut.cz> Date: Mon, 6 May 2024 16:05:42 +0200 Subject: [PATCH 10/12] :art: make category edit prettier --- .../ui/screens/category/EditCategoryScreen.kt | 98 ++++++++----------- 1 file changed, 42 insertions(+), 56 deletions(-) diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/EditCategoryScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/EditCategoryScreen.kt index 2fa203d..1c16eb8 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/EditCategoryScreen.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/EditCategoryScreen.kt @@ -3,32 +3,24 @@ package cz.cvut.fel.pda.financemanagement.ui.screens.category import androidx.compose.foundation.border import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.Close -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material.icons.outlined.Check import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.SegmentedButton import androidx.compose.material3.SegmentedButtonDefaults import androidx.compose.material3.SingleChoiceSegmentedButtonRow -import androidx.compose.material3.Switch import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.material3.TopAppBar -import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.getValue @@ -38,18 +30,18 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.RectangleShape -import androidx.compose.ui.text.toLowerCase +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController import cz.cvut.fel.pda.financemanagement.database.category.Category import cz.cvut.fel.pda.financemanagement.database.category.CategoryType +import cz.cvut.fel.pda.financemanagement.ui.components.BottomNavigation import cz.cvut.fel.pda.financemanagement.ui.components.ShowErrorScreen import cz.cvut.fel.pda.financemanagement.ui.components.ShowLoadingScreen +import cz.cvut.fel.pda.financemanagement.ui.components.TopBar import cz.cvut.fel.pda.financemanagement.ui.utils.AppViewModelProvider -import java.math.BigDecimal import java.util.Locale @Composable @@ -95,46 +87,26 @@ fun ShowCategorySuccessScreen( Scaffold( topBar = { - TopAppBar( - title = { Text("Category edit") }, - colors = TopAppBarDefaults.mediumTopAppBarColors( - containerColor = MaterialTheme.colorScheme.primaryContainer - ), - modifier = Modifier, - navigationIcon = { - IconButton(onClick = { navController.popBackStack() }) { - Icon( - imageVector = Icons.Outlined.Close, - contentDescription = "cs" - ) - } - }, - actions = { - TextButton( - onClick = { - categoryDetailViewModel.saveCategory( - type = type.value, - name = name.value - ) - navController.popBackStack() - }, - modifier = Modifier.padding(16.dp) - ) { - Text(text = "save") - } - } - ) + TopBar(navHostController = navController) }, content = { innerPadding -> Column ( modifier = Modifier .fillMaxWidth() + .padding(24.dp, 4.dp) .verticalScroll(rememberScrollState()) - .padding(innerPadding), // use innerPadding here - horizontalAlignment = Alignment.CenterHorizontally + .padding(innerPadding), + ) { - SingleChoiceSegmentedButtonRow { + val spaceModifier = Modifier + .padding(vertical = 14.dp) + Text( + modifier = spaceModifier, + style = MaterialTheme.typography.titleLarge.copy(fontWeight = FontWeight.Bold), + color = Color.Black, + text = if (category.id == 0L) "New Category" else "Edit Category") + SingleChoiceSegmentedButtonRow{ options.forEachIndexed{index, option -> SegmentedButton( selected = selectedOption.value == option, @@ -144,23 +116,37 @@ fun ShowCategorySuccessScreen( }, shape = SegmentedButtonDefaults.itemShape(index=index ,count = options.size) ) { - Text(text = option.name.lowercase(Locale.getDefault())) + Text(text = option.name.lowercase(Locale.getDefault()).replaceFirstChar { it.titlecase(Locale.getDefault()) }) } } } - - val spaceModifier = Modifier - .fillMaxWidth() - .padding(24.dp, 4.dp) + HorizontalDivider(modifier = spaceModifier) OutlinedTextField( - value = name.value, // Use the correct variable + value = name.value, onValueChange = { newValue -> - name.value = newValue // Use the correct variable + name.value = newValue }, - label = { Text("name") }, // Use the correct label - modifier = spaceModifier + label = { Text("name") } + ) + } + }, + floatingActionButton = { + FloatingActionButton( + onClick = { categoryDetailViewModel.saveCategory( + name = name.value, + type = type.value + ) + navController.popBackStack() }) + { + Icon( + Icons.Outlined.Check, + contentDescription = "Save", + modifier = Modifier.size(24.dp) ) } + }, + bottomBar = { + BottomNavigation(navController = navController) } ) } -- GitLab From e466c09ed66b3bf25f6af52a71e87c08613a2d6c Mon Sep 17 00:00:00 2001 From: dovicrad <dovicrad@fel.cvut.cz> Date: Mon, 6 May 2024 23:39:32 +0200 Subject: [PATCH 11/12] :art: add selectable category to transactions --- .../OfflineTransactionRepository.kt | 2 +- .../operation/transaction/TransactionDao.kt | 6 ++ .../transaction/TransactionRepository.kt | 13 +-- .../transactions/EditTransactionScreen.kt | 82 ++++++++++++++++--- .../TransactionDetailViewModel.kt | 5 +- .../transactions/TransactionsScreen.kt | 63 +++++++++----- 6 files changed, 125 insertions(+), 46 deletions(-) diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/OfflineTransactionRepository.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/OfflineTransactionRepository.kt index f27dd68..0719631 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/OfflineTransactionRepository.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/OfflineTransactionRepository.kt @@ -11,7 +11,7 @@ class OfflineTransactionRepository(private val transactionDao: TransactionDao): override fun getTransactionWithCategoryForIdStream(id: Long): Flow<TransactionWithCategory> = transactionDao.getCategoryWithTransactionForId(id) override fun getCategoryWithTransactions(): Flow<List<CategoryWithTransactions>> = transactionDao.getCategoryWithTransaction() - + override fun getTransactionsWithCategory(): Flow<List<TransactionWithCategory>> = transactionDao.getTransactionWithCategory() override suspend fun insertTransactions(vararg transactions: Transaction) = transactionDao.insertTransactions(*transactions) override suspend fun deleteTransaction(transaction: Transaction) = transactionDao.delete(transaction) override suspend fun deleteAllTransactions() = transactionDao.deleteAll() diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/TransactionDao.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/TransactionDao.kt index bd0d257..4801d00 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/TransactionDao.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/TransactionDao.kt @@ -26,6 +26,12 @@ interface TransactionDao { """) fun getCategoryWithTransactionForId(id: Long): Flow<TransactionWithCategory> + @Query(""" + SELECT * FROM `category` + JOIN `transaction` ON `transaction`.categoryId = `category`.id +""") + fun getTransactionWithCategory(): Flow<List<TransactionWithCategory>> + @Query(""" SELECT * FROM `category` INNER JOIN `transaction` ON `transaction`.categoryId = `category`.id diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/TransactionRepository.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/TransactionRepository.kt index 69e0220..1576e07 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/TransactionRepository.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/TransactionRepository.kt @@ -11,18 +11,7 @@ interface TransactionRepository { fun getTransactionWithCategoryForIdStream(id: Long): Flow<TransactionWithCategory> fun getCategoryWithTransactions(): Flow<List<CategoryWithTransactions>> - fun getTransactionsWithCategory(): Flow<List<TransactionWithCategory>> { - return getCategoryWithTransactions().map { categoryWithTransactionList -> - categoryWithTransactionList.flatMap { categoryWithTransaction -> - categoryWithTransaction.transaction.map { transaction -> - TransactionWithCategory( - transaction = transaction, - category = categoryWithTransaction.category - ) - } - } - } - } + fun getTransactionsWithCategory(): Flow<List<TransactionWithCategory>> suspend fun insertTransactions(vararg transactions: Transaction) suspend fun deleteTransaction(transaction: Transaction) suspend fun deleteAllTransactions() diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/EditTransactionScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/EditTransactionScreen.kt index a1a78ef..32d3b38 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/EditTransactionScreen.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/EditTransactionScreen.kt @@ -7,7 +7,10 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowDropDown import androidx.compose.material.icons.outlined.Close +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -30,9 +33,12 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController +import cz.cvut.fel.pda.financemanagement.database.category.Category import cz.cvut.fel.pda.financemanagement.model.TransactionWithCategory import cz.cvut.fel.pda.financemanagement.ui.components.ShowErrorScreen import cz.cvut.fel.pda.financemanagement.ui.components.ShowLoadingScreen +import cz.cvut.fel.pda.financemanagement.ui.screens.category.CategoriesUiState +import cz.cvut.fel.pda.financemanagement.ui.screens.category.CategoryViewModel import cz.cvut.fel.pda.financemanagement.ui.utils.AppViewModelProvider import java.math.BigDecimal @@ -41,8 +47,13 @@ fun EditTransactionScreen(navController: NavHostController, transitionId: Long?, transitionDetailViewModel: TransactionDetailViewModel = viewModel( factory = AppViewModelProvider.Factory -),){ + ), + categoryViewModel: CategoryViewModel = viewModel( + factory = AppViewModelProvider.Factory + ) + ,){ val uiState by transitionDetailViewModel.uiState.collectAsStateWithLifecycle() + val categoryUiState by categoryViewModel.categories.collectAsStateWithLifecycle() when (val state = uiState) { is TransactionWithCategoryUiState.Error -> { @@ -51,15 +62,29 @@ fun EditTransactionScreen(navController: NavHostController, onClick = { navController.popBackStack() } ) } - TransactionWithCategoryUiState.Loading -> { + is TransactionWithCategoryUiState.Loading -> { ShowLoadingScreen() } is TransactionWithCategoryUiState.Success -> { - ShowSuccessScreen( - state.transaction, - navController, - transitionDetailViewModel - ) + when(val categoryState = categoryUiState) { + is CategoriesUiState.Success -> { + ShowSuccessScreen( + state.transaction, + categoryState.itemList, + navController, + transitionDetailViewModel, + ) + } + is CategoriesUiState.Loading -> { + ShowLoadingScreen() + } + is CategoriesUiState.Error -> { + ShowErrorScreen( + errorMessage = categoryState.exception.message ?: "Error", + onClick = { navController.popBackStack() } + ) + } + } } } } @@ -68,10 +93,12 @@ fun EditTransactionScreen(navController: NavHostController, @Composable fun ShowSuccessScreen( transaction: TransactionWithCategory, + categories: List<Category>, navController: NavHostController, - transactionViewModel: TransactionDetailViewModel + transactionViewModel: TransactionDetailViewModel, ) { val amount = rememberSaveable { mutableStateOf(transaction.transaction.amount.toString()) } + val selectedCategory = remember{ mutableStateOf(categories.find { it.id == transaction.category.id } ?: categories[0]) } Scaffold( topBar = { @@ -94,6 +121,7 @@ fun ShowSuccessScreen( onClick = { transactionViewModel.saveTransaction( amount = BigDecimal(amount.value), + category = selectedCategory.value ) navController.popBackStack() }, @@ -104,20 +132,26 @@ fun ShowSuccessScreen( } ) }, + content = {innerPadding -> - EditTransactionDetailSuccessContent(innerPadding, amount) + EditTransactionDetailSuccessContent(innerPadding, amount, categories, selectedCategory) } ) } + @Composable -fun EditTransactionDetailSuccessContent(innerPadding: PaddingValues, amount: MutableState<String>) { - val openDialog = remember { mutableStateOf(false) } +fun EditTransactionDetailSuccessContent(innerPadding: PaddingValues, + amount: MutableState<String>, + categories: List<Category>, + selectedCategory: MutableState<Category> +) { + val showDropdown = remember { mutableStateOf(false) } Column ( modifier = Modifier .fillMaxWidth() .verticalScroll(rememberScrollState()) - .padding(innerPadding), // use innerPadding here + .padding(innerPadding), horizontalAlignment = Alignment.CenterHorizontally ) { val spaceModifier = Modifier @@ -133,5 +167,29 @@ fun EditTransactionDetailSuccessContent(innerPadding: PaddingValues, amount: Mut label = { Text("amount") }, modifier = spaceModifier ) + OutlinedTextField( + value = selectedCategory.value.name, + onValueChange = {}, + label = { Text("Category") }, + trailingIcon = { + IconButton(onClick = { showDropdown.value = true }) { + Icon(Icons.Filled.ArrowDropDown, contentDescription = null) + } + }, + readOnly = true + ) + DropdownMenu( + expanded = showDropdown.value, + onDismissRequest = { showDropdown.value = false } + ) { + categories.forEach { category -> + DropdownMenuItem(onClick = { + selectedCategory.value = category + showDropdown.value = false + }, text = { + Text(category.name) + }) + } + } } } \ No newline at end of file diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailViewModel.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailViewModel.kt index e3271b5..1b96a25 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailViewModel.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionDetailViewModel.kt @@ -46,14 +46,15 @@ class TransactionDetailViewModel( transactionId = id } fun saveTransaction( - amount: BigDecimal + amount: BigDecimal, + category: Category ) { viewModelScope.launch { transactionRepository.insertTransactions( Transaction( id = transactionId, amount = amount, - categoryId = 1L + categoryId = category.id ) ) } diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt index 4f10d61..f5687b1 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/transactions/TransactionsScreen.kt @@ -101,8 +101,10 @@ fun TransactionScreenContent( ) { val sortState = remember { mutableStateOf("Don't sort") } val sortedTransactions = when (sortState.value) { - "Sort by Amount" -> transactions.sortedBy { it.transaction.amount } - "Sort by Date" -> transactions.sortedBy { it.transaction.dateCreated } + "Sort by Amount Asc." -> transactions.sortedBy { it.transaction.amount } + "Sort by Date Asc." -> transactions.sortedBy { it.transaction.dateCreated } + "Sort by Amount Desc." -> transactions.sortedByDescending { it.transaction.amount } + "Sort by Date Desc." -> transactions.sortedByDescending { it.transaction.dateCreated } else -> transactions } @@ -127,25 +129,29 @@ fun TransactionScreenContent( @Composable fun SortDropdown(sortState: MutableState<String>) { var expanded by remember { mutableStateOf(false) } - val options = listOf("Don't sort", "Sort by Amount", "Sort by Date") + val options = listOf("Don't sort", "Sort by Amount Asc.", "Sort by Date Asc.", "Sort by Amount Desc.", "Sort by Date Desc.") - Box(Modifier.clickable { expanded = true }) { - Text(text = sortState.value) - Icon(Icons.Filled.ArrowDropDown, contentDescription = "Sort options") - DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) { - options.forEach { label -> - DropdownMenuItem( - onClick = { - sortState.value = label - expanded = false - }, - text = { Text(label) } - ) + Row(verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.clickable { expanded = true }) { + Box{ + Text(text = sortState.value) + DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) { + options.forEach { label -> + DropdownMenuItem( + onClick = { + sortState.value = label + expanded = false + }, + text = { Text(label) } + ) + } } } + Icon(Icons.Filled.ArrowDropDown, contentDescription = "Sort options") } } + @Composable fun TotalSumOfTransactions(transactions: List<TransactionWithCategory>) { val totalIncome = transactions.filter { it.category.type == CategoryType.INCOME }.sumOf { it.transaction.amount } @@ -200,8 +206,10 @@ fun TransactionsList( } } } + @Composable fun TransactionItem(transaction: Transaction, category: Category, navController: NavHostController) { + var showMenu by remember { mutableStateOf(false) } Row( modifier = Modifier .fillMaxWidth() @@ -209,17 +217,34 @@ fun TransactionItem(transaction: Transaction, category: Category, navController: .border(1.dp, Color.Gray, shape = RoundedCornerShape(3.dp)) .clickable { navController.navigate("${FinanceManagementScreens.TransactionsDetail.name}/${transaction.id}") } .padding(8.dp) - , horizontalArrangement = Arrangement.SpaceBetween + , horizontalArrangement = Arrangement.SpaceBetween ) { Column { - Text(text = category.name.toString(), style = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Bold)) + Text(text = category.name, style = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Bold)) Text( text = if (category.type == CategoryType.EXPENSE) "-${transaction.amount}" else transaction.amount.toString(), color = getCategoryColor(category), style = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Bold) ) } - IconButton(onClick = { /* Handle click event here */ }) { - Icon(Icons.Filled.MoreVert, contentDescription = "Options") + Box { + IconButton(onClick = { showMenu = true }) { + Icon(Icons.Filled.MoreVert, contentDescription = "Options") + } + DropdownMenu( + expanded = showMenu, + onDismissRequest = { showMenu = false } + ) { + DropdownMenuItem(onClick = { + navController.navigate("${FinanceManagementScreens.EditTransaction.name}/${transaction.id}") + showMenu = false + }, text = { Text("Edit") } + ) + DropdownMenuItem(onClick = { + // Handle delete transaction here + // transactionViewModel.deleteTransaction(transaction.id) + showMenu = false + }, text = { Text("Delete")}) + } } } } -- GitLab From de90814ca1d6e4a8fe56b91514de643950700824 Mon Sep 17 00:00:00 2001 From: dovicrad <dovicrad@fel.cvut.cz> Date: Mon, 6 May 2024 23:56:08 +0200 Subject: [PATCH 12/12] :art: prettier Categories List --- .../transaction/TransactionRepository.kt | 1 - .../ui/screens/category/CategoriesScreen.kt | 44 +++++++++++++++++-- .../ui/screens/category/EditCategoryScreen.kt | 1 - 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/TransactionRepository.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/TransactionRepository.kt index 1576e07..6093e05 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/TransactionRepository.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/database/operation/transaction/TransactionRepository.kt @@ -3,7 +3,6 @@ package cz.cvut.fel.pda.financemanagement.database.operation.transaction import cz.cvut.fel.pda.financemanagement.model.CategoryWithTransactions import cz.cvut.fel.pda.financemanagement.model.TransactionWithCategory import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map interface TransactionRepository { fun getAllTransactionsStream(): Flow<List<Transaction>> diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/CategoriesScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/CategoriesScreen.kt index 6f27a75..7064074 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/CategoriesScreen.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/CategoriesScreen.kt @@ -1,22 +1,31 @@ package cz.cvut.fel.pda.financemanagement.ui.screens.category import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row -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.material.icons.Icons import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.MoreVert +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable 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.text.TextStyle import androidx.compose.ui.text.font.FontWeight @@ -101,7 +110,36 @@ fun CategoriesList(categories: List<Category>, navController: NavHostController) @Composable fun CategoryItem(category: Category, navController: NavHostController) { - Row(modifier = Modifier.padding(8.dp).clickable { navController.navigate("${FinanceManagementScreens.CategoryDetail}/${category.id}")}){ - Text(text = category.name.toString().lowercase().replaceFirstChar { it.titlecase(Locale.ROOT)}) + var showMenu by remember { mutableStateOf(false) } + + Row( + modifier = Modifier.padding(8.dp).clickable { navController.navigate("${FinanceManagementScreens.CategoryDetail}/${category.id}") }, + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = category.name.lowercase().replaceFirstChar { it.titlecase(Locale.ROOT) }, + modifier = Modifier.weight(1f) + ) + Box(modifier = Modifier.weight(1f)) { + IconButton(onClick = { showMenu = true }) { + Icon(Icons.Filled.MoreVert, contentDescription = "Options") + } + DropdownMenu( + expanded = showMenu, + onDismissRequest = { showMenu = false } + ) { + DropdownMenuItem(onClick = { + navController.navigate("${FinanceManagementScreens.EditCategory.name}/${category.id}") + showMenu = false + }, text = { Text("Edit") } + ) + DropdownMenuItem(onClick = { + // Handle delete transaction here + // transactionViewModel.deleteTransaction(transaction.id) + showMenu = false + }, text = { Text("Delete")}) + } + } } } diff --git a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/EditCategoryScreen.kt b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/EditCategoryScreen.kt index 1c16eb8..601bc48 100644 --- a/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/EditCategoryScreen.kt +++ b/app/src/main/java/cz/cvut/fel/pda/financemanagement/ui/screens/category/EditCategoryScreen.kt @@ -1,6 +1,5 @@ package cz.cvut.fel.pda.financemanagement.ui.screens.category -import androidx.compose.foundation.border import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxWidth -- GitLab