diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index 26d33521af10bcc7fd8cea344038eaaeb78d0ef5..0000000000000000000000000000000000000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
deleted file mode 100644
index b589d56e9f285d8cfdc6c270853a5d439021a278..0000000000000000000000000000000000000000
--- a/.idea/compiler.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
-  <component name="CompilerConfiguration">
-    <bytecodeTargetLevel target="17" />
-  </component>
-</project>
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
deleted file mode 100644
index 32522c1e7054e664d0b44bf5c384d6e06213b9a5..0000000000000000000000000000000000000000
--- a/.idea/gradle.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
-  <component name="GradleSettings">
-    <option name="linkedExternalProjectsSettings">
-      <GradleProjectSettings>
-        <option name="externalProjectPath" value="$PROJECT_DIR$" />
-        <option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
-        <option name="modules">
-          <set>
-            <option value="$PROJECT_DIR$" />
-            <option value="$PROJECT_DIR$/app" />
-          </set>
-        </option>
-        <option name="resolveExternalAnnotations" value="false" />
-      </GradleProjectSettings>
-    </option>
-  </component>
-</project>
\ No newline at end of file
diff --git a/.idea/migrations.xml b/.idea/migrations.xml
deleted file mode 100644
index f8051a6f973e69a86e6f07f1a1c87f17a31c7235..0000000000000000000000000000000000000000
--- a/.idea/migrations.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
-  <component name="ProjectMigrations">
-    <option name="MigrateToGradleLocalJavaHome">
-      <set>
-        <option value="$PROJECT_DIR$" />
-      </set>
-    </option>
-  </component>
-</project>
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index 6ba496933e103b4a71a5bc5d279f57e859d31f91..0000000000000000000000000000000000000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
-  <component name="ExternalStorageConfigurationManager" enabled="true" />
-  <component name="ProjectRootManager" version="2" languageLevel="JDK_17">
-    <output url="file://$PROJECT_DIR$/build/classes" />
-  </component>
-  <component name="ProjectType">
-    <option name="id" value="Android" />
-  </component>
-</project>
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 35eb1ddfbbc029bcab630581847471d7f238ec53..0000000000000000000000000000000000000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
-  <component name="VcsDirectoryMappings">
-    <mapping directory="" vcs="Git" />
-  </component>
-</project>
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index a81dca6f0e4c492ea48931bc99fefd305f89ca0c..58f2ba955bc75a4d217f1c74c00b0b8418d30fa2 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -1,6 +1,8 @@
 plugins {
     id("com.android.application")
     id("org.jetbrains.kotlin.android")
+    id("com.google.devtools.ksp")
+    id("kotlin-android")
 }
 
 android {
@@ -44,11 +46,44 @@ dependencies {
     implementation("androidx.appcompat:appcompat:1.6.1")
     implementation("com.google.android.material:material:1.11.0")
     implementation("androidx.constraintlayout:constraintlayout:2.1.4")
+
     implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.7.0")
     implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0")
+
     implementation("androidx.navigation:navigation-fragment-ktx:2.7.7")
     implementation("androidx.navigation:navigation-ui-ktx:2.7.7")
+
+    //Pie Chart
+    implementation("com.github.PhilJay:MPAndroidChart:v3.1.0")
+
+    //Apache POI for saving file into xls/xlsx
+    val apachePoiVersion = "5.2.5"
+    implementation("org.apache.poi:poi:$apachePoiVersion")
+    implementation("org.apache.poi:poi-ooxml:$apachePoiVersion")
+
+    //Test tools
     testImplementation("junit:junit:4.13.2")
     androidTestImplementation("androidx.test.ext:junit:1.1.5")
     androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
+
+    //Room
+    val roomVersion = "2.6.1"
+    implementation("androidx.room:room-runtime:$roomVersion")
+    ksp("androidx.room:room-compiler:$roomVersion")
+    implementation("androidx.room:room-ktx:$roomVersion")
+
+    //Retrofit
+    val retrofitVersion = "2.9.0"
+    implementation("com.squareup.retrofit2:retrofit:$retrofitVersion")
+    implementation("com.squareup.retrofit2:converter-gson:$retrofitVersion")
+
+    //CameraX for scan
+    val cameraXVersion = "1.3.2"
+    implementation ("androidx.camera:camera-core:${cameraXVersion}")
+    implementation ("androidx.camera:camera-camera2:${cameraXVersion}")
+    implementation ("androidx.camera:camera-lifecycle:${cameraXVersion}")
+//    implementation ("androidx.camera:camera-video:${cameraXVersion}")
+
+    implementation ("androidx.camera:camera-view:${cameraXVersion}")
+    implementation ("androidx.camera:camera-extensions:${cameraXVersion}")
 }
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 89dbe27451e4914477fa322a441cbca14af35750..698869905a5e41c4d8a9455e70e52d76340783a5 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,8 +1,14 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools">
-
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-feature android:name="android.hardware.camera" />
+    <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
+        android:maxSdkVersion="28" />
     <application
+        android:name=".BondomanApp"
         android:allowBackup="true"
         android:dataExtractionRules="@xml/data_extraction_rules"
         android:fullBackupContent="@xml/backup_rules"
@@ -12,16 +18,37 @@
         android:supportsRtl="true"
         android:theme="@style/Theme.Exe_android"
         tools:targetApi="31">
+
+        <provider
+            android:name="androidx.core.content.FileProvider"
+            android:authorities="pbd.tubes.exe_android.provider"
+            android:exported="false"
+            android:grantUriPermissions="true">
+            <meta-data
+                android:name="android.support.FILE_PROVIDER_PATHS"
+                android:resource="@xml/file_paths" />
+        </provider>
+
         <activity
             android:name=".MainActivity"
             android:exported="true"
-            android:label="@string/app_name">
+            android:label="@string/app_name"
+            >
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
 
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+
+        <activity
+            android:name=".LoginActivity">
+        </activity>
+
+        <service android:name=".ui.login.TokenExpirationCheckService" />
+
+
+
     </application>
 
 </manifest>
\ No newline at end of file
diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png
new file mode 100644
index 0000000000000000000000000000000000000000..7d7b554629513161cff09b5b25a7662f18e9908a
Binary files /dev/null and b/app/src/main/ic_launcher-playstore.png differ
diff --git a/app/src/main/java/pbd/tubes/exe_android/BondomanApp.kt b/app/src/main/java/pbd/tubes/exe_android/BondomanApp.kt
new file mode 100644
index 0000000000000000000000000000000000000000..899c545f986ee251d725272d334a40cb87a73d6b
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/BondomanApp.kt
@@ -0,0 +1,13 @@
+package pbd.tubes.exe_android
+
+import android.app.Application
+import pbd.tubes.exe_android.data.AppContainer
+import pbd.tubes.exe_android.data.AppDataContainer
+
+class BondomanApp : 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/pbd/tubes/exe_android/MainActivity.kt b/app/src/main/java/pbd/tubes/exe_android/MainActivity.kt
index c3a88dc6598ba40110510b9caf2eacb496b512ca..90a8658bd6809fee9f42180adad9c682e01800a2 100644
--- a/app/src/main/java/pbd/tubes/exe_android/MainActivity.kt
+++ b/app/src/main/java/pbd/tubes/exe_android/MainActivity.kt
@@ -1,35 +1,128 @@
 package pbd.tubes.exe_android
 
+import android.content.Intent
+import android.content.res.Configuration
 import android.os.Bundle
-import com.google.android.material.bottomnavigation.BottomNavigationView
+import android.util.Log
+import androidx.appcompat.app.AlertDialog
 import androidx.appcompat.app.AppCompatActivity
 import androidx.navigation.findNavController
 import androidx.navigation.ui.AppBarConfiguration
 import androidx.navigation.ui.setupActionBarWithNavController
 import androidx.navigation.ui.setupWithNavController
+import com.google.android.material.navigation.NavigationBarView
 import pbd.tubes.exe_android.databinding.ActivityMainBinding
+import androidx.lifecycle.Observer
+import pbd.tubes.exe_android.ui.NetworkSensing.NetworkLiveData
+import pbd.tubes.exe_android.ui.login.TokenExpirationCheckService
+import pbd.tubes.exe_android.ui.transactions.AddTransactionFragment
 
 class MainActivity : AppCompatActivity() {
 
     private lateinit var binding: ActivityMainBinding
+    private val TAG_FRAGMENT = "broadcastFragment"
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
 
+        if (!isUserLoggedIn()) {
+            startActivity(Intent(this, LoginActivity::class.java))
+            finish()
+            return
+        }
+
+        val networkStatusLiveData = NetworkLiveData(this)
+        networkStatusLiveData.observe(this, Observer { isConnected ->
+            if (!isConnected) {
+                showNoInternetPopup()
+            }
+        })
+
+        Log.d("Main", "Ini masuk main")
+        activateBroadcastReceiverFragment()
         binding = ActivityMainBinding.inflate(layoutInflater)
         setContentView(binding.root)
+        val navController = findNavController(R.id.nav_host_fragment_activity_main)
+
+        // Checking current fragment to show/hide fab
+        // TODO(checking currentDestination id, this only check "navigation")
+        val currentDestinationId = navController.currentDestination!!.id
+        if (currentDestinationId != R.id.navigation_transactions){
+            Log.d("MyApp", "current fragment is $currentDestinationId, hiding fab")
+            binding.addFab.hide()
+        } else {
+            Log.d("MyApp", "current fragment is $currentDestinationId, showing fab")
+            binding.addFab.show()
+        }
 
-        val navView: BottomNavigationView = binding.navView
+        when (resources.configuration.orientation){
+            Configuration.ORIENTATION_LANDSCAPE -> {
+                binding.navViewRail?.let {
+                    setUpNavBar(it)
+                }
+                Log.d("MyApp", "Orientation is LANDSCAPE")
+            }
+            Configuration.ORIENTATION_PORTRAIT -> {
+                binding.navView?.let {
+                    setUpNavBar(it)
+                }
+                Log.d("MyApp", "Orientation is PORTRAIT")
+            }
+            else -> {
+                Log.d("MyApp", "What other config?")
+            }
+        }
 
+        binding.addFab.setOnClickListener {
+            navController.navigate(R.id.navigation_add_transaction)
+        }
+    }
+    private fun setUpNavBar(
+        navigationBarView: NavigationBarView
+    ){
         val navController = findNavController(R.id.nav_host_fragment_activity_main)
-        // Passing each menu ID as a set of Ids because each
-        // menu should be considered as top level destinations.
         val appBarConfiguration = AppBarConfiguration(
             setOf(
-                R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications
-            )
+                R.id.navigation_transactions, R.id.navigation_scan, R.id.navigation_chart, R.id.navigation_settings
+            ),
+//            fallbackOnNavigateUpListener = {navController.navigateUp()} //TODO(navigate back)
         )
         setupActionBarWithNavController(navController, appBarConfiguration)
-        navView.setupWithNavController(navController)
+        navigationBarView.setupWithNavController(navController)
+    }
+    override fun onPause() {
+        super.onPause()
+        val serviceIntent = Intent(this, TokenExpirationCheckService::class.java)
+        stopService(serviceIntent)
+
+    }
+    override fun onResume() {
+        super.onResume()
+        val serviceIntent = Intent(this, TokenExpirationCheckService::class.java)
+        startService(serviceIntent)
     }
+
+    private fun isUserLoggedIn(): Boolean {
+        val sharedPreferences = getSharedPreferences("user_session", MODE_PRIVATE)
+        return sharedPreferences.contains("token")
+    }
+
+    private fun showNoInternetPopup() {
+        AlertDialog.Builder(this)
+            .setTitle("No Internet Connection")
+            .setMessage("Please check your internet connection and try again.")
+            .setPositiveButton("OK", null)
+            .show()
+    }
+
+    private fun activateBroadcastReceiverFragment() {
+        // Check if the fragment is already added
+        if (supportFragmentManager.findFragmentByTag(TAG_FRAGMENT) == null) {
+            // If not added, add the fragment dynamically
+            val fragmentTransaction = supportFragmentManager.beginTransaction()
+            fragmentTransaction.add(AddTransactionFragment(), TAG_FRAGMENT)
+            fragmentTransaction.commit()
+        }
+    }
+
 }
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/data/AppContainer.kt b/app/src/main/java/pbd/tubes/exe_android/data/AppContainer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..473b6e983048d9318a817e340c03c4972088ee95
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/data/AppContainer.kt
@@ -0,0 +1,25 @@
+package pbd.tubes.exe_android.data
+
+import android.content.Context
+import pbd.tubes.exe_android.data.database.OfflineTransactionsRepository
+import pbd.tubes.exe_android.data.database.TransactionDatabase
+import pbd.tubes.exe_android.data.database.TransactionsRepository
+
+/**
+ * App container for Dependency injection.
+ */
+interface AppContainer {
+    val transactionRepository: TransactionsRepository
+}
+
+/**
+ * [AppContainer] implementation that provides instance of [OfflineTransactionsRepository]
+ */
+class AppDataContainer(private val context: Context) : AppContainer {
+    /**
+     * Implementation for [TransactionsRepository]
+     */
+    override val transactionRepository: TransactionsRepository by lazy {
+        OfflineTransactionsRepository(TransactionDatabase.getDatabase(context).transactionDao())
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/data/TokenResponse.kt b/app/src/main/java/pbd/tubes/exe_android/data/TokenResponse.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3bfada40dc81274885f010b0d44321593a11b125
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/data/TokenResponse.kt
@@ -0,0 +1,5 @@
+package pbd.tubes.exe_android.data
+
+data class TokenResponse (
+    val exp: Long
+)
diff --git a/app/src/main/java/pbd/tubes/exe_android/data/api/ApiService.kt b/app/src/main/java/pbd/tubes/exe_android/data/api/ApiService.kt
new file mode 100644
index 0000000000000000000000000000000000000000..0651ce4c5692a5fef9749a5379ee6230df315603
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/data/api/ApiService.kt
@@ -0,0 +1,31 @@
+package pbd.tubes.exe_android.data.api
+
+import okhttp3.MultipartBody
+import pbd.tubes.exe_android.data.TokenResponse
+import pbd.tubes.exe_android.data.api.login.LoginRequest
+import pbd.tubes.exe_android.data.api.login.LoginResponse
+import pbd.tubes.exe_android.data.api.upload.UploadResponse
+import retrofit2.Response
+import retrofit2.http.Body
+import retrofit2.http.Header
+import retrofit2.http.Multipart
+import retrofit2.http.POST
+import retrofit2.http.Part
+
+interface ApiService {
+    @POST("/api/auth/login")
+    suspend fun login(
+        @Body credentials: LoginRequest
+    ): Response<LoginResponse>
+
+    @Multipart
+    @POST("/api/bill/upload")
+    suspend fun uploadImage(
+        @Header("Authorization") authorization : String,
+        @Part imageFile : MultipartBody.Part
+    ): Response<UploadResponse>
+
+    @POST("/api/auth/token")
+    suspend fun decodeToken(
+        @Header("Authorization") authorization: String): Response<TokenResponse>
+}
diff --git a/app/src/main/java/pbd/tubes/exe_android/data/api/login/LoginRequest.kt b/app/src/main/java/pbd/tubes/exe_android/data/api/login/LoginRequest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3f9fa94bda16dcaa7ad68977278d6bfeefc2275a
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/data/api/login/LoginRequest.kt
@@ -0,0 +1,6 @@
+package pbd.tubes.exe_android.data.api.login
+
+data class LoginRequest(
+    val email: String,
+    val password: String
+)
diff --git a/app/src/main/java/pbd/tubes/exe_android/data/api/login/LoginResponse.kt b/app/src/main/java/pbd/tubes/exe_android/data/api/login/LoginResponse.kt
new file mode 100644
index 0000000000000000000000000000000000000000..71ee5e086c99113cb08b8f0607398fbab94b3eae
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/data/api/login/LoginResponse.kt
@@ -0,0 +1,5 @@
+package pbd.tubes.exe_android.data.api.login
+
+data class LoginResponse(
+    val token: String
+)
diff --git a/app/src/main/java/pbd/tubes/exe_android/data/api/upload/UploadResponse.kt b/app/src/main/java/pbd/tubes/exe_android/data/api/upload/UploadResponse.kt
new file mode 100644
index 0000000000000000000000000000000000000000..8093eae2eeedc244efd275cf66cdd05dc5c3b36a
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/data/api/upload/UploadResponse.kt
@@ -0,0 +1,31 @@
+package pbd.tubes.exe_android.data.api.upload
+
+import pbd.tubes.exe_android.models.Transaction
+import pbd.tubes.exe_android.models.TransactionCategory
+import java.time.LocalDate
+import java.time.ZoneId
+
+data class UploadResponse(
+    val items : Items
+)
+data class Items(
+    val items : List<Item>
+)
+data class Item(
+    val name : String,
+    val qty : Int,
+    val price : Double
+)
+
+fun Item.toTransaction() : Transaction {
+    return (
+            Transaction(
+                id = 0,
+                name = this.name,
+                category = TransactionCategory.PEMBELIAN,
+                date = LocalDate.now(ZoneId.systemDefault()),
+                lokasi = "", //TODO(Lokasi dengan maps)
+                nominal = this.qty * this.price
+            )
+    )
+}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/data/database/OfflineTransactionsRepository.kt b/app/src/main/java/pbd/tubes/exe_android/data/database/OfflineTransactionsRepository.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2d4486a1baec16d5e43bbc359f33e42280abe64c
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/data/database/OfflineTransactionsRepository.kt
@@ -0,0 +1,27 @@
+package pbd.tubes.exe_android.data.database
+
+import kotlinx.coroutines.flow.Flow
+import pbd.tubes.exe_android.data.database.helper.CategoryNominal
+import pbd.tubes.exe_android.models.Transaction
+import java.time.LocalDate
+
+class OfflineTransactionsRepository (private val transactionDao: TransactionDao) :
+    TransactionsRepository {
+    override fun getAllTransactionsStream(): Flow<List<Transaction>> = transactionDao.getAllTransactions()
+
+    override fun getTransactionStream(id: Int): Flow<Transaction?> = transactionDao.getTransaction(id)
+
+    override suspend fun insertTransaction(transaction: Transaction) = transactionDao.insert(transaction)
+
+    override suspend fun deleteTransaction(transaction: Transaction) = transactionDao.delete(transaction)
+
+    override suspend fun updateTransaction(transaction: Transaction) =transactionDao.update(transaction)
+
+    override suspend fun getAllTransactionsByRecentStream(): Flow<List<Transaction>> = transactionDao.getAllTransactionsByRecent()
+
+    override suspend fun getTransactionsAtDateStream(date: LocalDate): Flow<List<Transaction>> = transactionDao.getTransactionsByDate(date)
+
+    override suspend fun getTransactionsAtLocationStream(location: String): Flow<List<Transaction>> = transactionDao.getTransactionsByLocation(location)
+
+    override suspend fun getTransactionNominalsByCategoryStream(): Flow<List<CategoryNominal>> = transactionDao.getTransactionNominalsGroupByCategory()
+}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/data/database/TransactionDao.kt b/app/src/main/java/pbd/tubes/exe_android/data/database/TransactionDao.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3f0de38a6856169c0271e0c00a116dd3ee715bbb
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/data/database/TransactionDao.kt
@@ -0,0 +1,41 @@
+package pbd.tubes.exe_android.data.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
+import pbd.tubes.exe_android.data.database.helper.CategoryNominal
+import pbd.tubes.exe_android.models.Transaction
+import java.time.LocalDate
+
+@Dao
+interface TransactionDao {
+    @Insert(onConflict = OnConflictStrategy.IGNORE)
+    suspend fun insert(transaction: Transaction)
+
+    @Update
+    suspend fun update(transaction: Transaction)
+
+    @Delete
+    suspend fun delete(transaction: Transaction)
+
+    @Query("SELECT * from transactions WHERE id = :id")
+    fun getTransaction(id: Int): Flow<Transaction>
+
+    @Query("SELECT * from transactions")
+    fun getAllTransactions(): Flow<List<Transaction>>
+
+    @Query("SELECT * from transactions ORDER BY id DESC")
+    fun getAllTransactionsByRecent(): Flow<List<Transaction>>
+
+    @Query("SELECT * from transactions WHERE date = :date")
+    fun getTransactionsByDate(date: LocalDate): Flow<List<Transaction>>
+
+    @Query("SELECT * FROM transactions WHERE lokasi = :location")
+    fun getTransactionsByLocation(location: String): Flow<List<Transaction>>
+
+    @Query("SELECT category,SUM(nominal) FROM transactions GROUP BY category")
+    fun getTransactionNominalsGroupByCategory(): Flow<List<CategoryNominal>>
+}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/data/database/TransactionDatabase.kt b/app/src/main/java/pbd/tubes/exe_android/data/database/TransactionDatabase.kt
new file mode 100644
index 0000000000000000000000000000000000000000..bd64dac2390a91a0c9a05e2cf26d06586a805182
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/data/database/TransactionDatabase.kt
@@ -0,0 +1,29 @@
+package pbd.tubes.exe_android.data.database
+
+import android.content.Context
+import androidx.room.Database
+import androidx.room.Room
+import androidx.room.RoomDatabase
+import androidx.room.TypeConverters
+import pbd.tubes.exe_android.data.database.helper.DateConverter
+import pbd.tubes.exe_android.models.Transaction
+
+@Database(entities = [Transaction::class], version = 3, exportSchema = false)
+@TypeConverters(DateConverter::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, TransactionDatabase::class.java, "transaction_database")
+                    .fallbackToDestructiveMigration()
+                    .build()
+                    .also{ Instance = it }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/data/database/TransactionsRepository.kt b/app/src/main/java/pbd/tubes/exe_android/data/database/TransactionsRepository.kt
new file mode 100644
index 0000000000000000000000000000000000000000..47e457eeef1883e428be147bb5b82c5d5e50fe94
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/data/database/TransactionsRepository.kt
@@ -0,0 +1,40 @@
+package pbd.tubes.exe_android.data.database
+import kotlinx.coroutines.flow.Flow
+import pbd.tubes.exe_android.data.database.helper.CategoryNominal
+import pbd.tubes.exe_android.models.Transaction
+import java.time.LocalDate
+
+/**
+ * Repository that provides insert, update, delete, and retrieve of [Transaction] from a given data source.
+ */
+interface TransactionsRepository {
+    /**
+     * Retrieve all the transactions from the given data source.
+     */
+    fun getAllTransactionsStream(): Flow<List<Transaction>>
+
+    /**
+     * Retrieve an Transaction from the given data source that matches with the [id].
+     */
+    fun getTransactionStream(id: Int): Flow<Transaction?>
+
+    /**
+     * Insert Transaction in the data source
+     */
+    suspend fun insertTransaction(transaction: Transaction)
+
+    /**
+     * Delete Transaction from the data source
+     */
+    suspend fun deleteTransaction(transaction: Transaction)
+
+    /**
+     * Update Transaction in the data source
+     */
+    suspend fun updateTransaction(transaction: Transaction)
+    suspend fun getAllTransactionsByRecentStream(): Flow<List<Transaction>>
+
+    suspend fun getTransactionsAtDateStream(date: LocalDate): Flow<List<Transaction>>
+    suspend fun getTransactionsAtLocationStream(location: String): Flow<List<Transaction>>
+    suspend fun getTransactionNominalsByCategoryStream(): Flow<List<CategoryNominal>>
+}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/data/database/helper/CategoryNominal.kt b/app/src/main/java/pbd/tubes/exe_android/data/database/helper/CategoryNominal.kt
new file mode 100644
index 0000000000000000000000000000000000000000..234cda4c913275e1db245e979ce8225794cadc4b
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/data/database/helper/CategoryNominal.kt
@@ -0,0 +1,20 @@
+package pbd.tubes.exe_android.data.database.helper
+
+import androidx.room.ColumnInfo
+import pbd.tubes.exe_android.models.TransactionCategory
+import java.util.EnumMap
+
+data class CategoryNominal(
+    @ColumnInfo("category")
+    val category: TransactionCategory,
+    @ColumnInfo("SUM(nominal)")
+    val nominal: Double,
+)
+
+fun List<CategoryNominal>.toCategoryNominalMap(): EnumMap<TransactionCategory, Double> {
+    val ret = EnumMap<TransactionCategory, Double>(TransactionCategory::class.java)
+    this.forEach {
+        ret[it.category] = it.nominal
+    }
+    return ret
+}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/data/database/helper/DateConverter.kt b/app/src/main/java/pbd/tubes/exe_android/data/database/helper/DateConverter.kt
new file mode 100644
index 0000000000000000000000000000000000000000..786b48c46c4443070574b694fe39c790ae099846
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/data/database/helper/DateConverter.kt
@@ -0,0 +1,17 @@
+package pbd.tubes.exe_android.data.database.helper
+
+import androidx.room.TypeConverter
+import java.time.LocalDate
+
+class DateConverter {
+    @TypeConverter
+    fun fromDate(date: LocalDate): Long {
+        return date.toEpochDay()
+    }
+
+    @TypeConverter
+    fun toDate(epochDay: Long): LocalDate {
+        return LocalDate.ofEpochDay(epochDay)
+    }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/models/Transaction.kt b/app/src/main/java/pbd/tubes/exe_android/models/Transaction.kt
new file mode 100644
index 0000000000000000000000000000000000000000..03a6733454457852ed647a3b3fec3dfe49feebea
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/models/Transaction.kt
@@ -0,0 +1,19 @@
+package pbd.tubes.exe_android.models
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import java.time.LocalDate
+
+@Entity(tableName = "transactions")
+data class Transaction (
+    @PrimaryKey(autoGenerate = true)
+    val id: Int = 0,
+    val name: String,
+    val nominal: Double,
+    val category: TransactionCategory,
+    val lokasi: String, //TODO(Lokasi dari lokasi device)
+    val date: LocalDate,
+)
+
+enum class TransactionCategory {
+    PEMBELIAN, PEMASUKAN
+}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/ui/NetworkSensing/NetworkLiveData.kt b/app/src/main/java/pbd/tubes/exe_android/ui/NetworkSensing/NetworkLiveData.kt
new file mode 100644
index 0000000000000000000000000000000000000000..6ea0da0a580b52a76896493a604ca8fe4bc4697e
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/ui/NetworkSensing/NetworkLiveData.kt
@@ -0,0 +1,42 @@
+package pbd.tubes.exe_android.ui.NetworkSensing
+
+import android.content.Context
+import android.net.ConnectivityManager
+import android.net.Network
+import android.net.NetworkRequest
+import androidx.lifecycle.LiveData
+
+class NetworkLiveData(private val context: Context) : LiveData<Boolean>() {
+
+    private val connectivityManager =
+        context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+
+    private val networkCallback = object : ConnectivityManager.NetworkCallback() {
+        override fun onAvailable(network: Network) {
+            postValue(true)
+        }
+
+        override fun onLost(network: Network) {
+            postValue(false)
+        }
+    }
+
+    override fun onActive() {
+        super.onActive()
+        registerNetworkCallback()
+    }
+
+    override fun onInactive() {
+        super.onInactive()
+        unregisterNetworkCallback()
+    }
+
+    private fun registerNetworkCallback() {
+        val builder = NetworkRequest.Builder()
+        connectivityManager.registerNetworkCallback(builder.build(), networkCallback)
+    }
+
+    private fun unregisterNetworkCallback() {
+        connectivityManager.unregisterNetworkCallback(networkCallback)
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/ui/chart/ChartFragment.kt b/app/src/main/java/pbd/tubes/exe_android/ui/chart/ChartFragment.kt
new file mode 100644
index 0000000000000000000000000000000000000000..691ed45b47d16e1c0934c5943286228ba534a998
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/ui/chart/ChartFragment.kt
@@ -0,0 +1,84 @@
+package pbd.tubes.exe_android.ui.chart
+
+import android.graphics.Color
+import android.graphics.Typeface
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.content.ContextCompat
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import com.github.mikephil.charting.charts.PieChart
+import com.github.mikephil.charting.data.PieData
+import com.github.mikephil.charting.data.PieDataSet
+import com.github.mikephil.charting.formatter.DefaultValueFormatter
+import pbd.tubes.exe_android.R
+import pbd.tubes.exe_android.databinding.FragmentChartBinding
+import pbd.tubes.exe_android.models.TransactionCategory
+
+class ChartFragment : Fragment() {
+
+    private var _binding: FragmentChartBinding? = null
+    private val viewModel : ChartViewModel by viewModels (factoryProducer = {ChartViewModel.Factory})
+
+    // This property is only valid between onCreateView and
+    // onDestroyView.
+    private val binding get() = _binding!!
+
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View {
+        _binding = FragmentChartBinding.inflate(inflater, container, false)
+        val root: View = binding.root
+
+        val pieChart: PieChart = binding.pieChart
+//        pieChart.setUsePercentValues(true)
+        pieChart.dragDecelerationFrictionCoef = 0.95f
+        pieChart.isDrawHoleEnabled = true
+        pieChart.setEntryLabelColor(Color.BLACK)
+        pieChart.setEntryLabelTextSize(12f)
+        pieChart.setTransparentCircleAlpha(110)
+        pieChart.setTransparentCircleColor(Color.WHITE)
+        pieChart.description = null
+
+        //TODO(Specify legend)
+        viewModel.fetchTotalByCategory()
+        viewModel.nominalSumByCategory?.observe(viewLifecycleOwner){
+            val dataArrayList = viewModel.dataToArrayList()
+            val colorsArrayList = ArrayList<Int>()
+
+            for (entry in dataArrayList) {
+                val color = when (entry.label) {
+                    TransactionCategory.PEMBELIAN.toString() ->
+                        ContextCompat.getColor(requireContext(), R.color.pembelian_color)
+                    TransactionCategory.PEMASUKAN.toString() ->
+                        ContextCompat.getColor(requireContext(), R.color.pemasukan_color)
+                    else -> Color.BLACK // Default color
+                }
+                entry.label = entry.label.lowercase().replaceFirstChar { it.uppercase() }
+                colorsArrayList.add(color)
+            }
+            val dataSet = PieDataSet(dataArrayList, "Transaksi")
+            dataSet.colors = colorsArrayList
+            val data = PieData(dataSet)
+
+            data.setValueFormatter(DefaultValueFormatter(2))
+            data.setValueTextSize(15f)
+            data.setValueTypeface(Typeface.DEFAULT_BOLD)
+            data.setValueTextColor(Color.BLACK)
+            pieChart.centerText = String.format("Total: %.2f", viewModel.totalTransaksi())
+
+            pieChart.setData(data)
+            pieChart.invalidate()
+        }
+        return root
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        _binding = null
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/ui/chart/ChartViewModel.kt b/app/src/main/java/pbd/tubes/exe_android/ui/chart/ChartViewModel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..34cd601cd5a9c2357bf221b48dc01af41258d410
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/ui/chart/ChartViewModel.kt
@@ -0,0 +1,75 @@
+package pbd.tubes.exe_android.ui.chart
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.asLiveData
+import androidx.lifecycle.viewModelScope
+import androidx.lifecycle.viewmodel.CreationExtras
+import com.github.mikephil.charting.data.PieEntry
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+import pbd.tubes.exe_android.BondomanApp
+import pbd.tubes.exe_android.data.database.TransactionsRepository
+import pbd.tubes.exe_android.data.database.helper.toCategoryNominalMap
+import pbd.tubes.exe_android.models.TransactionCategory
+import java.util.EnumMap
+
+class ChartViewModel(
+    private val transactionsRepository: TransactionsRepository
+) : ViewModel() {
+
+//    private var _chart = MutableLiveData<>()
+//    val chart: LiveData<> = _chart
+
+    var nominalSumByCategory : LiveData<EnumMap<TransactionCategory, Double>>? = null
+    fun fetchTotalByCategory() {
+        viewModelScope.launch {
+            nominalSumByCategory = transactionsRepository
+                .getTransactionNominalsByCategoryStream()
+                .map {
+                    it.toCategoryNominalMap()
+                }.asLiveData()
+        }
+    }
+    fun totalTransaksi(): Double {
+        var sum = 0.0
+        for (value in nominalSumByCategory!!.value!!.values) {
+            sum += value
+        }
+        return sum
+    }
+
+    fun dataToArrayList() : ArrayList<PieEntry> {
+        val data = ArrayList<PieEntry>()
+        for (i in nominalSumByCategory!!.value!!.keys){
+            data.add(
+                PieEntry(
+                    nominalSumByCategory!!.value!![i]!!.toFloat(),
+                    i.toString()
+                )
+            )
+        }
+        return data
+    }
+
+
+    companion object {
+        class ChartViewModelFactory(
+        ) : ViewModelProvider.Factory {
+            @Suppress("UNCHECKED_CAST")
+            override fun <T : ViewModel> create(
+                modelClass: Class<T>,
+                extras: CreationExtras
+            ): T {
+                val application = checkNotNull(extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY])
+    //                val savedStateHandle = extras.createSavedStateHandle()
+                return ChartViewModel(
+                    (application as BondomanApp).container.transactionRepository
+    //                    savedStateHandle
+                ) as T
+            }
+        }
+        val Factory: ViewModelProvider.Factory = ChartViewModelFactory()
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/ui/dashboard/DashboardFragment.kt b/app/src/main/java/pbd/tubes/exe_android/ui/dashboard/DashboardFragment.kt
deleted file mode 100644
index ebfc8bdfe88f39b0a5ae4826579dd755b92fecc3..0000000000000000000000000000000000000000
--- a/app/src/main/java/pbd/tubes/exe_android/ui/dashboard/DashboardFragment.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-package pbd.tubes.exe_android.ui.dashboard
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import androidx.fragment.app.Fragment
-import androidx.lifecycle.ViewModelProvider
-import pbd.tubes.exe_android.databinding.FragmentDashboardBinding
-
-class DashboardFragment : Fragment() {
-
-    private var _binding: FragmentDashboardBinding? = null
-
-    // This property is only valid between onCreateView and
-    // onDestroyView.
-    private val binding get() = _binding!!
-
-    override fun onCreateView(
-        inflater: LayoutInflater,
-        container: ViewGroup?,
-        savedInstanceState: Bundle?
-    ): View {
-        val dashboardViewModel =
-            ViewModelProvider(this).get(DashboardViewModel::class.java)
-
-        _binding = FragmentDashboardBinding.inflate(inflater, container, false)
-        val root: View = binding.root
-
-        val textView: TextView = binding.textDashboard
-        dashboardViewModel.text.observe(viewLifecycleOwner) {
-            textView.text = it
-        }
-        return root
-    }
-
-    override fun onDestroyView() {
-        super.onDestroyView()
-        _binding = null
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/ui/dashboard/DashboardViewModel.kt b/app/src/main/java/pbd/tubes/exe_android/ui/dashboard/DashboardViewModel.kt
deleted file mode 100644
index 7e5f2a18ca08e30fd58bbef3ca8ffd4c1abcd5b7..0000000000000000000000000000000000000000
--- a/app/src/main/java/pbd/tubes/exe_android/ui/dashboard/DashboardViewModel.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package pbd.tubes.exe_android.ui.dashboard
-
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.ViewModel
-
-class DashboardViewModel : ViewModel() {
-
-    private val _text = MutableLiveData<String>().apply {
-        value = "This is dashboard Fragment"
-    }
-    val text: LiveData<String> = _text
-}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/ui/home/HomeFragment.kt b/app/src/main/java/pbd/tubes/exe_android/ui/home/HomeFragment.kt
deleted file mode 100644
index f5fb5db20df2ca88a7a3d2662c5a98714c62a111..0000000000000000000000000000000000000000
--- a/app/src/main/java/pbd/tubes/exe_android/ui/home/HomeFragment.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-package pbd.tubes.exe_android.ui.home
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import androidx.fragment.app.Fragment
-import androidx.lifecycle.ViewModelProvider
-import pbd.tubes.exe_android.databinding.FragmentHomeBinding
-
-class HomeFragment : Fragment() {
-
-    private var _binding: FragmentHomeBinding? = null
-
-    // This property is only valid between onCreateView and
-    // onDestroyView.
-    private val binding get() = _binding!!
-
-    override fun onCreateView(
-        inflater: LayoutInflater,
-        container: ViewGroup?,
-        savedInstanceState: Bundle?
-    ): View {
-        val homeViewModel =
-            ViewModelProvider(this).get(HomeViewModel::class.java)
-
-        _binding = FragmentHomeBinding.inflate(inflater, container, false)
-        val root: View = binding.root
-
-        val textView: TextView = binding.textHome
-        homeViewModel.text.observe(viewLifecycleOwner) {
-            textView.text = it
-        }
-        return root
-    }
-
-    override fun onDestroyView() {
-        super.onDestroyView()
-        _binding = null
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/ui/home/HomeViewModel.kt b/app/src/main/java/pbd/tubes/exe_android/ui/home/HomeViewModel.kt
deleted file mode 100644
index 7adbeb5fec7474d816c69e5233b021d03c7fee3f..0000000000000000000000000000000000000000
--- a/app/src/main/java/pbd/tubes/exe_android/ui/home/HomeViewModel.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package pbd.tubes.exe_android.ui.home
-
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.ViewModel
-
-class HomeViewModel : ViewModel() {
-
-    private val _text = MutableLiveData<String>().apply {
-        value = "This is home Fragment"
-    }
-    val text: LiveData<String> = _text
-}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/ui/login/LoginActivity.kt b/app/src/main/java/pbd/tubes/exe_android/ui/login/LoginActivity.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c899832b1cb996acbfb35b4ebe92d5f568f23b2b
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/ui/login/LoginActivity.kt
@@ -0,0 +1,126 @@
+package pbd.tubes.exe_android
+
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
+import android.widget.Button
+import android.widget.EditText
+import android.widget.Toast
+import androidx.appcompat.app.AppCompatActivity
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import pbd.tubes.exe_android.data.api.ApiService
+import pbd.tubes.exe_android.data.api.login.LoginRequest
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+import androidx.appcompat.app.AlertDialog
+import androidx.lifecycle.Observer
+import pbd.tubes.exe_android.ui.NetworkSensing.NetworkLiveData
+import pbd.tubes.exe_android.ui.login.TokenExpirationCheckService
+
+class LoginActivity : AppCompatActivity() {
+
+    private lateinit var apiService: ApiService
+    private lateinit var emailEditText: EditText
+    private lateinit var passwordEditText: EditText
+    private lateinit var loginButton: Button
+
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        val networkStatusLiveData = NetworkLiveData(this)
+        networkStatusLiveData.observe(this, Observer { isConnected ->
+            if (!isConnected) {
+                showNoInternetPopup()
+            }
+        })
+
+        setContentView(R.layout.activity_login)
+
+        val retrofit = Retrofit.Builder()
+            .baseUrl(BASE_URL)
+            .addConverterFactory(GsonConverterFactory.create())
+            .build()
+
+        apiService = retrofit.create(ApiService::class.java)
+
+        loginButton = findViewById(R.id.loginButton)
+        emailEditText = findViewById(R.id.emailEditText)
+        passwordEditText = findViewById(R.id.passwordEditText)
+
+        loginButton.setOnClickListener {
+//            val email = emailEditText.text.toString()
+//            val password = passwordEditText.text.toString()
+            val email = "1@std.stei.itb.ac.id"
+            val password = "password_1"
+            val loginRequest = LoginRequest(email, password)
+            login(loginRequest)
+
+        }
+    }
+    private fun login(loginRequest: LoginRequest) {
+        CoroutineScope(Dispatchers.IO).launch {
+            try {
+                val response = apiService.login(loginRequest)
+                if (response.isSuccessful) {
+                    val token = response.body()?.token
+                    token?.let { tkn ->
+
+                        val responseDecode = apiService.decodeToken("Bearer $tkn")
+
+                        if (responseDecode.isSuccessful) {
+                            val expire = responseDecode.body()?.exp
+                            expire?.let {exp ->
+                                startActivity(Intent(this@LoginActivity, MainActivity::class.java))
+                                saveTokenExpiration(exp)
+                                saveToken(tkn)
+                                val serviceIntent = Intent(this@LoginActivity, TokenExpirationCheckService::class.java)
+                                startService(serviceIntent)
+                                finish()
+                            }
+                        }
+                        else {
+                            Log.d("Failed Token", "Failed Get decoded token (error response)")
+                        }
+
+
+                    }
+                    Toast.makeText(baseContext,
+                        "Log In Success!",
+                        Toast.LENGTH_SHORT).show()
+                } else {
+                    Toast.makeText(baseContext,
+                        "Log In Failed!",
+                        Toast.LENGTH_SHORT).show()
+                    Log.d("MyApp", "Failed login (error response)")
+                }
+            } catch (e: Exception) {
+                Log.d("MyApp", "$e")
+            }
+        }
+    }
+
+    private fun saveToken(token: String) {
+        val sharedPreferences = getSharedPreferences("user_session", MODE_PRIVATE)
+        sharedPreferences.edit().putString("token", token).apply()
+    }
+
+    private fun saveTokenExpiration(expirationTime: Long) {
+        val sharedPreferences = getSharedPreferences("user_session", MODE_PRIVATE)
+        sharedPreferences.edit().putLong("expiration_time", expirationTime).apply()
+    }
+
+    private fun showNoInternetPopup() {
+        AlertDialog.Builder(this)
+            .setTitle("No Internet Connection")
+            .setMessage("Please check your internet connection and try again.")
+            .setPositiveButton("OK", null)
+            .show()
+    }
+
+    companion object {
+        private const val BASE_URL = "https://pbd-backend-2024.vercel.app"
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/ui/login/TokenExpirationService.kt b/app/src/main/java/pbd/tubes/exe_android/ui/login/TokenExpirationService.kt
new file mode 100644
index 0000000000000000000000000000000000000000..5711465e797474d7b1e97a4329632c7e09f056ac
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/ui/login/TokenExpirationService.kt
@@ -0,0 +1,49 @@
+package pbd.tubes.exe_android.ui.login
+import android.app.Notification
+import android.app.Service
+import android.content.Intent
+import android.os.Handler
+import android.os.IBinder
+import android.os.Looper
+import android.util.Log
+import pbd.tubes.exe_android.LoginActivity
+
+class TokenExpirationCheckService : Service() {
+
+    private val sharedPreferences by lazy {
+        getSharedPreferences("user_session", MODE_PRIVATE)
+    }
+
+    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+        checkTokenExpiration()
+        return START_STICKY
+    }
+
+    private fun checkTokenExpiration() {
+        val expirationTime = sharedPreferences.getLong("expiration_time", 0)
+        val currentTime = System.currentTimeMillis() / 1000
+        val timeDifference = expirationTime - currentTime
+//        Log.d("Difference", "difference is $timeDifference")
+
+        if (currentTime > expirationTime) {
+            redirectToLogin()
+            stopSelf()
+            return
+        }
+
+        Handler(Looper.getMainLooper()).postDelayed({
+            checkTokenExpiration()
+        }, (timeDifference) * 1000)
+    }
+
+    private fun redirectToLogin() {
+        val loginIntent = Intent(this, LoginActivity::class.java)
+        loginIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+        sharedPreferences.edit().clear().apply()
+        startActivity(loginIntent)
+    }
+
+    override fun onBind(intent: Intent?): IBinder? {
+        return null
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/ui/notifications/NotificationsFragment.kt b/app/src/main/java/pbd/tubes/exe_android/ui/notifications/NotificationsFragment.kt
deleted file mode 100644
index 35e8a2a87bfe2b9f6f7fee932e14b8d9c2951971..0000000000000000000000000000000000000000
--- a/app/src/main/java/pbd/tubes/exe_android/ui/notifications/NotificationsFragment.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-package pbd.tubes.exe_android.ui.notifications
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import androidx.fragment.app.Fragment
-import androidx.lifecycle.ViewModelProvider
-import pbd.tubes.exe_android.databinding.FragmentNotificationsBinding
-
-class NotificationsFragment : Fragment() {
-
-    private var _binding: FragmentNotificationsBinding? = null
-
-    // This property is only valid between onCreateView and
-    // onDestroyView.
-    private val binding get() = _binding!!
-
-    override fun onCreateView(
-        inflater: LayoutInflater,
-        container: ViewGroup?,
-        savedInstanceState: Bundle?
-    ): View {
-        val notificationsViewModel =
-            ViewModelProvider(this).get(NotificationsViewModel::class.java)
-
-        _binding = FragmentNotificationsBinding.inflate(inflater, container, false)
-        val root: View = binding.root
-
-        val textView: TextView = binding.textNotifications
-        notificationsViewModel.text.observe(viewLifecycleOwner) {
-            textView.text = it
-        }
-        return root
-    }
-
-    override fun onDestroyView() {
-        super.onDestroyView()
-        _binding = null
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/ui/notifications/NotificationsViewModel.kt b/app/src/main/java/pbd/tubes/exe_android/ui/notifications/NotificationsViewModel.kt
deleted file mode 100644
index b4a13729318609509a70c65c61a718c7dc6fddea..0000000000000000000000000000000000000000
--- a/app/src/main/java/pbd/tubes/exe_android/ui/notifications/NotificationsViewModel.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package pbd.tubes.exe_android.ui.notifications
-
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.ViewModel
-
-class NotificationsViewModel : ViewModel() {
-
-    private val _text = MutableLiveData<String>().apply {
-        value = "This is notifications Fragment"
-    }
-    val text: LiveData<String> = _text
-}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/ui/scan/ScanFragment.kt b/app/src/main/java/pbd/tubes/exe_android/ui/scan/ScanFragment.kt
new file mode 100644
index 0000000000000000000000000000000000000000..1fa15afc16f4f210841ab0a2747cd8311e43bf68
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/ui/scan/ScanFragment.kt
@@ -0,0 +1,339 @@
+package pbd.tubes.exe_android.ui.scan
+
+import android.Manifest
+import android.content.ContentValues
+import android.content.Context
+import android.content.Context.MODE_PRIVATE
+import android.content.pm.PackageManager
+import android.net.Uri
+import android.os.Bundle
+import android.provider.MediaStore
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Toast
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.PickVisualMediaRequest
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.ImageAnalysis
+import androidx.camera.core.ImageCapture
+import androidx.camera.core.ImageCapture.OutputFileResults
+import androidx.camera.core.ImageCaptureException
+import androidx.camera.core.Preview
+import androidx.camera.lifecycle.ProcessCameraProvider
+import androidx.core.content.ContextCompat
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.lifecycleScope
+import kotlinx.coroutines.launch
+import okhttp3.MediaType
+import okhttp3.MultipartBody
+import okhttp3.OkHttpClient
+import okhttp3.RequestBody
+import pbd.tubes.exe_android.R
+import pbd.tubes.exe_android.data.api.ApiService
+import pbd.tubes.exe_android.databinding.FragmentScanBinding
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+import java.io.File
+import java.text.SimpleDateFormat
+import java.util.Locale
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Executors
+
+
+class ScanFragment : Fragment() {
+
+    private var _binding: FragmentScanBinding? = null
+
+    // This property is only valid between onCreateView and
+    // onDestroyView.
+    private val binding get() = _binding!!
+
+    private val retrofit: Retrofit by lazy {
+        Retrofit.Builder()
+            .baseUrl(BASE_URL)
+            .client(OkHttpClient.Builder().build())
+            .addConverterFactory(GsonConverterFactory.create())
+            .build()
+    }
+    private val apiService: ApiService by lazy {
+        retrofit.create(ApiService::class.java)
+    }
+
+    private var imageCapture: ImageCapture? = null
+    private lateinit var cameraExecutor: ExecutorService
+    private var pickMedia: ActivityResultLauncher<PickVisualMediaRequest>? = null
+    private val viewModel : ScanViewModel by viewModels(factoryProducer = { ScanViewModel.Factory })
+    override fun onAttach(context: Context) {
+        super.onAttach(context)
+        pickMedia = registerForActivityResult(
+            ActivityResultContracts.PickVisualMedia()
+        ) {
+            if (it != null){
+                imagePreview(it)
+                Log.d("MyApp", "Picked picture, selected URI: $it")
+            } else {
+                Log.d("MyApp", "No picture picked")
+            }
+        }
+    }
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View {
+
+        _binding = FragmentScanBinding.inflate(inflater, container, false)
+        val root: View = binding.root
+        if (allPermissionsGranted()) {
+            startCamera()
+        } else {
+            requestPermissions()
+        }
+
+        binding.imageCaptureButton.setOnClickListener { takePhoto() }
+        binding.pickImageButton.setOnClickListener{ imageChooser() }
+        cameraExecutor = Executors.newSingleThreadExecutor()
+        return root
+    }
+    // Function to get the actual file path from URI
+    fun getPathFromUri(context: Context, uri: Uri): String? {
+        var filePath: String? = null
+        when (uri.scheme) {
+            // For "file" scheme URIs
+            "file" -> {
+                filePath = uri.path
+            }
+            // For "content" scheme URIs (API level 19 and above)
+            "content" -> {
+                filePath =
+                    getImagePathFromUri(context, uri)
+            }
+        }
+        return filePath
+    }
+
+    private fun getImagePathFromUri(context: Context, uri: Uri): String? {
+        val projection = arrayOf(MediaStore.Images.Media.DATA)
+        val cursor = context.contentResolver.query(uri, projection, null, null, null)
+        cursor?.use {
+            if (it.moveToFirst()) {
+                val columnIndex = it.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
+                return it.getString(columnIndex)
+            }
+        }
+        return null
+    }
+    private fun uploadImage(imageUri: Uri){
+        lifecycleScope.launch {
+            val file = File(getPathFromUri(requireContext(), imageUri)!!)
+            val fileBody : RequestBody = RequestBody.create(MediaType.parse("image/*"), file)
+            val imagePart: MultipartBody.Part  = MultipartBody.Part.createFormData("file", file.getName(), fileBody)
+            val sharedPreferences = requireContext().getSharedPreferences("user_session", MODE_PRIVATE)
+            try {
+                val uploadResponse = apiService.uploadImage(
+                    "Bearer ${
+                        sharedPreferences.getString("token", null)!!}",
+                    imagePart)
+                if (uploadResponse.isSuccessful) {
+                    Log.d("MyApp", "Succeeded receiving response, adding transaction")
+                } else {
+                    Log.d("MyApp", "Receiving response failed, ${uploadResponse.message()}")
+                }
+                Log.d("MyApp", uploadResponse.body()?.items?.items.toString())
+                viewModel.submitTransactions(uploadResponse.body()?.items?.items!!)
+
+            } catch (e : Exception) {
+                Log.e("MyApp", "jir exception: $e")
+            }
+        }
+        Toast.makeText(requireContext(), "Image successfully uploaded", Toast.LENGTH_SHORT).show()
+    }
+
+    private fun imagePreview(imageUri: Uri){
+        binding.imagePreview.setImageURI(imageUri)
+        binding.scanFragment.removeView(binding.viewFinder)
+        binding.imageCaptureButton.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_done_24))
+        binding.imageCaptureButton.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.pemasukan_color))
+        binding.pickImageButton.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_redo_24))
+        binding.pickImageButton.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.pembelian_color))
+
+        binding.pickImageButton.setOnClickListener {
+            resetState()
+        }
+        binding.imageCaptureButton.setOnClickListener {
+            resetState()
+            uploadImage(imageUri)
+        }
+    }
+
+    private fun resetState(){
+        binding.scanFragment.addView(binding.viewFinder)
+
+        binding.pickImageButton.background = ContextCompat.getDrawable(requireContext(), R.drawable.rounded_rectangle)
+        binding.pickImageButton.setImageDrawable(ContextCompat.getDrawable(requireContext(),R.drawable.ic_image_24))
+        binding.pickImageButton.setOnClickListener { imageChooser() }
+
+        binding.imagePreview.setImageDrawable(null)
+        binding.imagePreview.visibility = View.VISIBLE
+
+        binding.imageCaptureButton.background = ContextCompat.getDrawable(requireContext(), R.drawable.camera_button)
+        binding.imageCaptureButton.setImageDrawable(null)
+        binding.imageCaptureButton.setOnClickListener { takePhoto() }
+        //TODO(Delete file from URI)
+    }
+    private fun imageChooser() {
+        pickMedia!!.launch(PickVisualMediaRequest(
+            ActivityResultContracts.PickVisualMedia.ImageOnly
+        ))
+    }
+
+    private fun takePhoto() {
+        // Get a stable reference of the modifiable image capture use case
+        val imageCapture = imageCapture ?: return
+        // Create time stamped name and MediaStore entry.
+        name = SimpleDateFormat(FILENAME_FORMAT, Locale.ENGLISH)
+            .format(System.currentTimeMillis())
+        val contentValues = ContentValues().apply {
+            put(MediaStore.MediaColumns.DISPLAY_NAME, name)
+            put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
+            put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/temp")
+        }
+
+        // Create output options object which contains file + metadata
+        val outputOptions = ImageCapture.OutputFileOptions
+            .Builder(requireContext().contentResolver,
+                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+                contentValues)
+            .build()
+
+        // Set up image capture listener, which is triggered after photo has
+        // been taken
+        imageCapture.takePicture(
+            outputOptions,
+            ContextCompat.getMainExecutor(requireContext()),
+            object : ImageCapture.OnImageSavedCallback {
+                override fun onError(exc: ImageCaptureException) {
+                    Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
+                }
+                override fun
+                        onImageSaved(output: OutputFileResults){
+                    imagePreview(output.savedUri!!)
+                    Log.d(TAG, "Photo capture success")
+                }
+            }
+        )
+    }
+
+    private fun startCamera() {
+        val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())
+
+        cameraProviderFuture.addListener({
+            // Used to bind the lifecycle of cameras to the lifecycle owner
+            val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
+
+            // Preview
+            val preview = Preview.Builder()
+                .build()
+                .also {
+                    it.setSurfaceProvider(binding.viewFinder.surfaceProvider)
+                }
+
+            imageCapture = ImageCapture.Builder()
+                .build()
+
+            val imageAnalyzer = ImageAnalysis.Builder()
+                .build()
+//                .also {
+//                    it.setAnalyzer(cameraExecutor, LuminosityAnalyzer { luma ->
+//                        Log.d(TAG, "Average luminosity: $luma")
+//                    })
+//                }
+
+            // Select back camera as a default
+            val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
+
+            try {
+                // Unbind use cases before rebinding
+                cameraProvider.unbindAll()
+
+                // Bind use cases to camera
+                cameraProvider.bindToLifecycle(
+                    this, cameraSelector, preview, imageCapture, imageAnalyzer)
+
+            } catch(exc: Exception) {
+                Log.e(TAG, "Use case binding failed", exc)
+            }
+
+        }, ContextCompat.getMainExecutor(requireContext()))
+    }
+
+    private fun requestPermissions() {
+        activityResultLauncher.launch(REQUIRED_PERMISSIONS)
+    }
+
+    private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
+        ContextCompat.checkSelfPermission(
+            requireContext(), it) == PackageManager.PERMISSION_GRANTED
+    }
+
+    private val activityResultLauncher =
+        registerForActivityResult(
+            ActivityResultContracts.RequestMultiplePermissions())
+        { permissions ->
+            // Handle Permission granted/rejected
+            var permissionGranted = true
+            permissions.entries.forEach {
+                if (it.key in REQUIRED_PERMISSIONS && !it.value)
+                    permissionGranted = false
+            }
+            if (!permissionGranted) {
+                Toast.makeText(requireContext(),
+                    "Permission request denied",
+                    Toast.LENGTH_SHORT).show()
+            } else {
+                startCamera()
+            }
+        }
+//    private class LuminosityAnalyzer(private val listener: LumaListener) : ImageAnalysis.Analyzer {
+//
+//        private fun ByteBuffer.toByteArray(): ByteArray {
+//            rewind()    // Rewind the buffer to zero
+//            val data = ByteArray(remaining())
+//            get(data)   // Copy the buffer into a byte array
+//            return data // Return the byte array
+//        }
+//
+//        override fun analyze(image: ImageProxy) {
+//
+//            val buffer = image.planes[0].buffer
+//            val data = buffer.toByteArray()
+//            val pixels = data.map { it.toInt() and 0xFF }
+//            val luma = pixels.average()
+//
+//            listener(luma)
+//
+//            image.close()
+//        }
+//    }
+    override fun onDestroyView() {
+        super.onDestroyView()
+        _binding = null
+        cameraExecutor.shutdown()
+    }
+    companion object {
+        private const val BASE_URL = "https://pbd-backend-2024.vercel.app"
+        private const val TAG = "MyApp"
+        private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
+        private var name :String? = null
+        private val REQUIRED_PERMISSIONS =
+            mutableListOf (
+                Manifest.permission.CAMERA,
+            ).apply {
+//                add(Manifest.permission.WRITE_EXTERNAL_STORAGE) //DEPRECATED, only for sdk <= 28, use the MediaStore.createWriteRequest intent
+            }.toTypedArray()
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/ui/scan/ScanViewModel.kt b/app/src/main/java/pbd/tubes/exe_android/ui/scan/ScanViewModel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..95f6fe8c3a7f989005b5497f4252bc21dacce37e
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/ui/scan/ScanViewModel.kt
@@ -0,0 +1,41 @@
+package pbd.tubes.exe_android.ui.scan
+
+import android.util.Log
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
+import androidx.lifecycle.viewModelScope
+import androidx.lifecycle.viewmodel.CreationExtras
+import kotlinx.coroutines.launch
+import pbd.tubes.exe_android.BondomanApp
+import pbd.tubes.exe_android.data.api.upload.Item
+import pbd.tubes.exe_android.data.api.upload.toTransaction
+import pbd.tubes.exe_android.data.database.TransactionsRepository
+
+class ScanViewModel(private val transactionsRepository : TransactionsRepository) : ViewModel() {
+    fun submitTransactions(response: List<Item>){
+        viewModelScope.launch {
+            response.map {
+                transactionsRepository.insertTransaction(it.toTransaction())
+                Log.d("MyApp", "data : $it.name $it.nominal $it.category $it.category $it.location $it.date")
+            }
+        }
+    }
+
+    companion object {
+        val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
+            @Suppress("UNCHECKED_CAST")
+            override fun <T : ViewModel> create(
+                modelClass: Class<T>,
+                extras: CreationExtras)
+                    : T {
+                val application = checkNotNull(extras[APPLICATION_KEY])
+//                val savedStateHandle = extras.createSavedStateHandle()
+                return ScanViewModel(
+                    (application as BondomanApp).container.transactionRepository,
+//                    savedStateHandle
+                ) as T
+            }
+        }
+    }
+}
diff --git a/app/src/main/java/pbd/tubes/exe_android/ui/settings/SettingsFragment.kt b/app/src/main/java/pbd/tubes/exe_android/ui/settings/SettingsFragment.kt
new file mode 100644
index 0000000000000000000000000000000000000000..8fb01230815f1357eda374e56cf29b5ea1d4e06c
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/ui/settings/SettingsFragment.kt
@@ -0,0 +1,143 @@
+package pbd.tubes.exe_android.ui.settings
+
+import android.content.Context.MODE_PRIVATE
+import android.content.Intent
+import android.os.Bundle
+import android.os.Environment
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import android.widget.Toast
+import androidx.core.content.FileProvider
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentManager
+import androidx.fragment.app.viewModels
+import androidx.navigation.fragment.findNavController
+import pbd.tubes.exe_android.LoginActivity
+import pbd.tubes.exe_android.R
+import pbd.tubes.exe_android.databinding.FragmentSettingsBinding
+import java.io.File
+import kotlin.random.Random
+
+class SettingsFragment : Fragment() {
+
+    private var _binding: FragmentSettingsBinding? = null
+    private lateinit var fragmentManager : FragmentManager
+
+    // This property is only valid between onCreateView and
+    // onDestroyView.
+    private val binding get() = _binding!!
+    private val viewModel : SettingsViewModel by viewModels(factoryProducer = {SettingsViewModel.Factory})
+
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View {
+        _binding = FragmentSettingsBinding.inflate(inflater, container, false)
+        val root: View = binding.root
+        binding.saveButton.setOnCheckedChangeListener{ _, isChecked ->
+            if (isChecked){
+                binding.formatDaftarTransaksi.visibility = View.VISIBLE
+                binding.radioFormat.visibility = View.VISIBLE
+                binding.confirmSaveButton.visibility = View.VISIBLE
+            } else{
+                binding.formatDaftarTransaksi.visibility = View.GONE
+                binding.radioFormat.visibility = View.GONE
+                binding.confirmSaveButton.visibility = View.GONE
+            }
+        }
+        binding.confirmSaveButton.setOnClickListener{
+            when (binding.radioFormat.checkedRadioButtonId){
+                R.id.xls_choice -> viewModel.saveXLS()
+                R.id.xlsx_choice -> viewModel.saveXLSX()
+            }
+            Toast.makeText(requireContext(), "File is saved", Toast.LENGTH_SHORT).show()
+        }
+        return root
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        viewModel.fetchData()
+        viewModel.transactionList?.observe(viewLifecycleOwner){
+            it?.let {
+                Log.d("MyApp", "data for saving has been loaded")
+            }
+        }
+
+        val sendButton = view.findViewById<Button>(R.id.send_button)
+        sendButton.setOnClickListener {
+            val email = "13521025@std.stei.itb.ac.id"
+            val subject = "Transaction Details"
+            val text = "Please find attached the transaction details."
+
+            sendTransactionsViaEmail(email, subject, text)
+
+        }
+
+        val randomize = view.findViewById<Button>(R.id.randomize_button)
+        randomize.setOnClickListener {
+
+            val randomNumber = generateRandomNumber(1000, 9999)
+            val intent = Intent("pbd.tubes.exe_android.ACTION_RANDOMIZE_TRANSAKSI").apply {
+                putExtra("randomNumber", randomNumber)
+            }
+            requireContext().sendBroadcast(intent)
+            Log.d("Onpause setting", "Settinganjay")
+            findNavController().navigate(R.id.navigation_add_transaction)
+
+        }
+
+
+        val logoutButton = view.findViewById<Button>(R.id.log_out_button)
+
+
+        logoutButton.setOnClickListener {
+            logout()
+        }
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        _binding = null
+    }
+
+    override fun onPause() {
+        super.onPause()
+
+    }
+    private fun logout() {
+        val sharedPreferences = requireContext().getSharedPreferences("user_session", MODE_PRIVATE)
+        sharedPreferences.edit().clear().apply()
+        val intent = Intent(requireContext(), LoginActivity::class.java)
+        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+        startActivity(intent)
+        requireActivity().finish()
+    }
+
+    private fun sendTransactionsViaEmail(email: String, subject: String, text: String) {
+        val downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
+
+        val filePath = File(downloadsDir, "daftar_transaksi.xlsx")
+        val fileUri = FileProvider.getUriForFile(
+            requireContext(),
+            requireContext().applicationContext.packageName + ".provider",
+            filePath
+        )
+        val intent = Intent(Intent.ACTION_SEND)
+        intent.type = "message/rfc822"
+        intent.putExtra(Intent.EXTRA_EMAIL, arrayOf(email))
+        intent.putExtra(Intent.EXTRA_SUBJECT, subject)
+        intent.putExtra(Intent.EXTRA_TEXT, text)
+        intent.putExtra(Intent.EXTRA_STREAM, fileUri)
+        intent.setPackage("com.google.android.gm")
+        requireContext().startActivity(intent)
+    }
+
+    private fun generateRandomNumber(min: Int, max: Int): Int {
+        return Random.nextInt(min, max + 1) // Generates a random number between min (inclusive) and max (inclusive)
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/ui/settings/SettingsViewModel.kt b/app/src/main/java/pbd/tubes/exe_android/ui/settings/SettingsViewModel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..94ce8f88cb436c0ef1144d916a34c8085ecf5109
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/ui/settings/SettingsViewModel.kt
@@ -0,0 +1,129 @@
+package pbd.tubes.exe_android.ui.settings
+
+import android.os.Environment
+import android.util.Log
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.asLiveData
+import androidx.lifecycle.viewModelScope
+import androidx.lifecycle.viewmodel.CreationExtras
+import kotlinx.coroutines.launch
+import org.apache.poi.hssf.usermodel.HSSFWorkbook
+import org.apache.poi.ss.usermodel.CellStyle
+import org.apache.poi.ss.usermodel.DataFormat
+import org.apache.poi.ss.util.DateFormatConverter
+import org.apache.poi.xssf.usermodel.XSSFWorkbook
+import pbd.tubes.exe_android.BondomanApp
+import pbd.tubes.exe_android.data.database.TransactionsRepository
+import pbd.tubes.exe_android.models.Transaction
+import java.io.File
+import java.io.FileOutputStream
+import java.util.Locale
+
+class SettingsViewModel(
+    private val transactionsRepository: TransactionsRepository
+) : ViewModel() {
+
+    var transactionList: LiveData<List<Transaction>>? = null
+
+    fun saveXLSX(){
+        val workbook = XSSFWorkbook()
+        val workSheet = workbook.createSheet("Daftar Transaksi")
+        val excelFormatPattern = DateFormatConverter.convert(Locale.ENGLISH, "dd MMMM yyyy")
+        val dateCellStyle: CellStyle = workbook.createCellStyle()
+        val poiFormat: DataFormat = workbook.createDataFormat()
+        dateCellStyle.dataFormat = poiFormat.getFormat(excelFormatPattern)
+
+        var row = workSheet.createRow(0)
+        row.createCell(0).setCellValue("Tanggal")
+        row.createCell(1).setCellValue("Kategori Transaksi")
+        row.createCell(2).setCellValue("Nominal Transaksi")
+        row.createCell(3).setCellValue("Nama Transaksi")
+        row.createCell(4).setCellValue("Lokasi")
+
+        for (i in transactionList!!.value!!.indices){
+            row = workSheet.createRow(i+1)
+            val dateCell = row.createCell(0)
+            dateCell.setCellValue(transactionList!!.value!![i].date)
+            dateCell.setCellStyle(dateCellStyle)
+            row.createCell(1).setCellValue(transactionList!!.value!![i].category.toString())
+            row.createCell(2).setCellValue(transactionList!!.value!![i].nominal)
+            row.createCell(3).setCellValue(transactionList!!.value!![i].name)
+            row.createCell(4).setCellValue(transactionList!!.value!![i].lokasi)
+        }
+
+//        val tempFile = createTempFile("daftar_transaksi_", ".xlsx")
+        val directory = File(Environment.getExternalStorageDirectory(), "Download")
+        if (!directory.exists()) {
+            directory.mkdirs()
+        }
+        val file = File(directory, "daftar_transaksi.xlsx")
+
+        FileOutputStream(file).use {workbook.write(it)}
+        workbook.close()
+        Log.d("MyApp", "xlsx saved")
+    }
+    fun saveXLS(){
+        val workbook = HSSFWorkbook()
+        val workSheet = workbook.createSheet("Daftar Transaksi")
+        val excelFormatPattern = DateFormatConverter.convert(Locale.ENGLISH, "dd MMMM yyyy")
+//        val dateCellStyle: CellStyle = workbook.createCellStyle()
+//        val poiFormat: DataFormat = workbook.createDataFormat()
+//        dateCellStyle.dataFormat = poiFormat.getFormat(excelFormatPattern)
+
+        var row = workSheet.createRow(0)
+        row.createCell(0).setCellValue("Tanggal")
+        row.createCell(1).setCellValue("Kategori Transaksi")
+        row.createCell(2).setCellValue("Nominal Transaksi")
+        row.createCell(3).setCellValue("Nama Transaksi")
+        row.createCell(4).setCellValue("Lokasi")
+        for (i in transactionList!!.value!!.indices){
+            row = workSheet.createRow(i+1)
+            val dateCell = row.createCell(0)
+            dateCell.setCellValue(transactionList!!.value!![i].date)
+//            dateCell.setCellStyle(dateCellStyle)
+            row.createCell(1).setCellValue(transactionList!!.value!![i].category.toString())
+            row.createCell(2).setCellValue(transactionList!!.value!![i].nominal)
+            row.createCell(3).setCellValue(transactionList!!.value!![i].name)
+            row.createCell(4).setCellValue(transactionList!!.value!![i].lokasi)
+        }
+
+//        val tempFile = createTempFile("daftar_transaksi_", ".xls")
+
+        val directory = File(Environment.getExternalStorageDirectory(), "Download")
+        if (!directory.exists()) {
+            directory.mkdirs()
+        }
+        val file = File(directory, "daftar_transaksi.xls")
+
+        FileOutputStream(file).use {workbook.write(it)}
+        workbook.close()
+        Log.d("MyApp", "xls saved")
+    }
+
+    fun fetchData(){
+        viewModelScope.launch {
+            transactionList = transactionsRepository.getAllTransactionsByRecentStream().asLiveData()
+        }
+    }
+
+    companion object {
+        val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
+            @Suppress("UNCHECKED_CAST")
+            override fun <T : ViewModel> create(
+                modelClass: Class<T>,
+                extras: CreationExtras
+            )
+                    : T {
+                val application = checkNotNull(extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY])
+//                val savedStateHandle = extras.createSavedStateHandle()
+                return SettingsViewModel(
+                    (application as BondomanApp).container.transactionRepository,
+//                    savedStateHandle
+                ) as T
+            }
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/ui/transactions/AddTransactionFragment.kt b/app/src/main/java/pbd/tubes/exe_android/ui/transactions/AddTransactionFragment.kt
new file mode 100644
index 0000000000000000000000000000000000000000..71255c71d48a18bef549700855c82ab9d0026980
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/ui/transactions/AddTransactionFragment.kt
@@ -0,0 +1,134 @@
+package pbd.tubes.exe_android.ui.transactions
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.Build
+import android.os.Bundle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.RadioButton
+import android.widget.Toast
+import androidx.annotation.RequiresApi
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.lifecycleScope
+import androidx.navigation.fragment.findNavController
+import com.google.android.material.textfield.TextInputLayout
+import kotlinx.coroutines.launch
+import pbd.tubes.exe_android.R
+import pbd.tubes.exe_android.databinding.FragmentAddTransactionBinding
+import pbd.tubes.exe_android.models.TransactionCategory
+
+class AddTransactionFragment : Fragment() {
+    private var _binding: FragmentAddTransactionBinding? = null
+    private val receiver = RandomizeTransactionReceiver()
+    // This property is only valid between onCreateView and
+    // onDestroyView.
+    private val binding get() = _binding!!
+    private val viewModel: AddTransactionViewModel by viewModels(factoryProducer = { AddTransactionViewModel.Factory })
+    @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        val intentFilter = IntentFilter("pbd.tubes.exe_android.ACTION_RANDOMIZE_TRANSAKSI")
+        requireContext().registerReceiver(receiver, intentFilter, Context.RECEIVER_NOT_EXPORTED)
+    }
+
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View {
+        _binding = FragmentAddTransactionBinding.inflate(inflater, container, false)
+        val root: View = binding.root
+//        onRandomNumberReceived(0)
+
+        val transactionName: TextInputLayout = binding.transactionNameInputLayout
+        viewModel.trName.observe(viewLifecycleOwner) {
+            transactionName.editText?.setText(it)
+        }
+
+        val transactionNominal: TextInputLayout = binding.transactionNominalInputLayout
+        viewModel.trNominal.observe(viewLifecycleOwner) {
+            transactionNominal.editText?.setText(it)
+        }
+        if (random != null){
+            transactionNominal.editText?.setText(random.toString())
+        } else {
+            transactionNominal.editText?.setText("")
+        }
+
+        val transactionCategoryPemasukan: RadioButton = binding.radioPemasukan
+        viewModel.trCategoryMasuk.observe(viewLifecycleOwner){
+            transactionCategoryPemasukan.isChecked = (it == true)
+        }
+        val transactionCategoryPembelian: RadioButton = binding.radioPembelian
+        viewModel.trCategoryBeli.observe(viewLifecycleOwner){
+            transactionCategoryPembelian.isChecked = (it == true)
+        }
+
+        val transactionLocation: TextInputLayout = binding.transactionLocationInputLayout
+        viewModel.trLocation.observe(viewLifecycleOwner) {
+            transactionLocation.editText?.setText(it)
+        }
+
+
+        fun chooseCategory() : TransactionCategory? {
+            if (transactionCategoryPemasukan.isChecked){
+                return TransactionCategory.PEMASUKAN
+            } else if (transactionCategoryPembelian.isChecked){
+                return TransactionCategory.PEMBELIAN
+            }
+            return null
+        }
+
+        binding.submitButton.setOnClickListener{
+            viewLifecycleOwner.lifecycleScope.launch {
+                try {
+                    viewModel.submitTransaction(
+                        transactionName.editText!!.text.toString(),
+                        transactionNominal.editText!!.text.toString(),
+                        chooseCategory()!!,
+                        transactionLocation.editText!!.text.toString(),
+                    )
+                    Toast.makeText(requireContext(),
+                        "Transaction ${transactionName.editText!!.text} is added",
+                        Toast.LENGTH_SHORT).show()
+                    findNavController().navigate(R.id.navigation_transactions)
+                } catch (e : NullPointerException) {
+                    Toast.makeText(requireContext(),
+                        "Error! Please fill in all fields",
+                        Toast.LENGTH_SHORT).show()
+                    Log.e("NULL", "Input has null")
+                }
+            }
+        }
+
+        return root
+    }
+
+    inner class RandomizeTransactionReceiver :  BroadcastReceiver(){
+        override fun onReceive(context: Context, intent: Intent){
+            val randomNumber = intent.getIntExtra("randomNumber", 0)
+            random = randomNumber
+//            Log.d("Ini random", randomNumber.toString())
+        }
+    }
+
+    companion object{
+        private var random: Int? = null
+    }
+    override fun onPause() {
+        super.onPause()
+        random = null
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        requireContext().unregisterReceiver(receiver)
+        _binding = null
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/ui/transactions/AddTransactionViewModel.kt b/app/src/main/java/pbd/tubes/exe_android/ui/transactions/AddTransactionViewModel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2bb06a7652bcd99704ff57a29879fc3a58d24ffa
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/ui/transactions/AddTransactionViewModel.kt
@@ -0,0 +1,66 @@
+package pbd.tubes.exe_android.ui.transactions
+
+import android.util.Log
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
+import androidx.lifecycle.viewmodel.CreationExtras
+import pbd.tubes.exe_android.BondomanApp
+import pbd.tubes.exe_android.data.database.TransactionsRepository
+import pbd.tubes.exe_android.models.Transaction
+import pbd.tubes.exe_android.models.TransactionCategory
+import java.time.LocalDate
+import java.time.ZoneId
+
+class AddTransactionViewModel(private val transactionsRepository : TransactionsRepository) : ViewModel() {
+
+    private val _trName = MutableLiveData<String>()
+    private val _trNominal = MutableLiveData<String>()
+    private val _trCategoryBeli = MutableLiveData<Boolean>()
+    private val _trCategoryMasuk = MutableLiveData<Boolean>()
+    private val _trLocation = MutableLiveData<String>()
+
+    val trName: LiveData<String> = _trName
+    val trNominal: LiveData<String> = _trNominal
+    val trCategoryBeli: LiveData<Boolean> = _trCategoryBeli
+    val trCategoryMasuk: LiveData<Boolean> = _trCategoryMasuk
+    val trLocation: LiveData<String> = _trLocation
+    suspend fun submitTransaction(
+        name: String,
+        nominal: String,
+        category: TransactionCategory,
+        location: String
+    ){
+        val date = LocalDate.now(ZoneId.systemDefault())
+        transactionsRepository.insertTransaction(
+            Transaction(
+                id = 0,
+                name = name,
+                nominal = nominal.toDoubleOrNull() ?: 0.0,
+                category = category,
+                lokasi = location,
+                date =  date,
+            )
+        )
+        Log.d("MyApp", "data : $name $nominal $category $category $location $date")
+    }
+
+    companion object {
+        val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
+            @Suppress("UNCHECKED_CAST")
+            override fun <T : ViewModel> create(
+                modelClass: Class<T>,
+                extras: CreationExtras)
+            : T {
+                val application = checkNotNull(extras[APPLICATION_KEY])
+//                val savedStateHandle = extras.createSavedStateHandle()
+                return AddTransactionViewModel(
+                    (application as BondomanApp).container.transactionRepository,
+//                    savedStateHandle
+                ) as T
+            }
+        }
+    }
+}
diff --git a/app/src/main/java/pbd/tubes/exe_android/ui/transactions/EditTransactionFragment.kt b/app/src/main/java/pbd/tubes/exe_android/ui/transactions/EditTransactionFragment.kt
new file mode 100644
index 0000000000000000000000000000000000000000..205a94fb2b0ae1421626f652598ce9139b329560
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/ui/transactions/EditTransactionFragment.kt
@@ -0,0 +1,30 @@
+package pbd.tubes.exe_android.ui.transactions
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import pbd.tubes.exe_android.databinding.FragmentEditTransactionBinding
+import pbd.tubes.exe_android.models.Transaction
+
+class EditTransactionFragment(transaction: Transaction) : Fragment() {
+    private var _binding : FragmentEditTransactionBinding? = null
+    private val binding get() = _binding!!
+    val viewModel: EditTransactionViewModel by viewModels(factoryProducer = {
+        EditTransactionViewModel.Factory(transaction)
+    })
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View {
+        _binding = FragmentEditTransactionBinding.inflate(inflater, container, false)
+        val root: View = binding.root
+
+
+
+        return root
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/ui/transactions/EditTransactionViewModel.kt b/app/src/main/java/pbd/tubes/exe_android/ui/transactions/EditTransactionViewModel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3a06cf028826271f68802edd3319a670e220950b
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/ui/transactions/EditTransactionViewModel.kt
@@ -0,0 +1,34 @@
+package pbd.tubes.exe_android.ui.transactions
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.viewmodel.CreationExtras
+import pbd.tubes.exe_android.BondomanApp
+import pbd.tubes.exe_android.data.database.TransactionsRepository
+import pbd.tubes.exe_android.models.Transaction
+
+class EditTransactionViewModel(
+    private val transactionsRepository: TransactionsRepository,
+    private val transaction: Transaction
+) : ViewModel() {
+    companion object {
+        class EditTransactionViewModelFactory(
+            private val transaction: Transaction
+        ) : ViewModelProvider.Factory {
+            @Suppress("UNCHECKED_CAST")
+            override fun <T : ViewModel> create(
+                modelClass: Class<T>,
+                extras: CreationExtras
+            ): T {
+                val application = checkNotNull(extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY])
+                return EditTransactionViewModel(
+                    (application as BondomanApp).container.transactionRepository,
+                    transaction)
+                    as T
+            }
+        }
+        val Factory: (Transaction) -> ViewModelProvider.Factory = {
+            EditTransactionViewModelFactory(it)
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/ui/transactions/TransactionsFragment.kt b/app/src/main/java/pbd/tubes/exe_android/ui/transactions/TransactionsFragment.kt
new file mode 100644
index 0000000000000000000000000000000000000000..342426974bc1c5de496b2957b7e9928b0a4eb3df
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/ui/transactions/TransactionsFragment.kt
@@ -0,0 +1,80 @@
+package pbd.tubes.exe_android.ui.transactions
+
+import android.os.Bundle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Toast
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import pbd.tubes.exe_android.databinding.FragmentTransactionsBinding
+import pbd.tubes.exe_android.ui.transactions.component.TransactionListAdapter
+
+class TransactionsFragment : Fragment() {
+    private lateinit var recyclerView: RecyclerView
+    private lateinit var transactionListAdapter: TransactionListAdapter
+    private var _binding: FragmentTransactionsBinding? = null
+    private val viewModel: TransactionsViewModel by viewModels(
+        factoryProducer = { TransactionsViewModel.Factory }
+    )
+    // This property is only valid between onCreateView and
+    // onDestroyView.
+    private val binding get() = _binding!!
+
+    // TODO(filter by location by opening Google Maps)
+    // TODO(transaction order is only by date and still reversed on the same day)
+    // possible solution: order by ID instead
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View {
+        _binding = FragmentTransactionsBinding.inflate(inflater, container, false)
+
+        val root: View = binding.root
+
+        recyclerView = binding.transactionRecycler
+        transactionListAdapter = TransactionListAdapter(
+            onDeleteClickListener = {
+                Log.d("MyApp", "Delete button is clicked")
+                viewModel.deleteItem(it)
+                Toast.makeText(requireContext(),
+                    "Transaction ${it.name} has been deleted",
+                    Toast.LENGTH_SHORT).show()
+            },
+            onEditClickListener = {
+                Log.d("MyApp", "Edit button is clicked")
+//                TODO("Navigation with arguments is not implemented yet")
+//                val bundle = bundleOf("transaction" to it)
+//                findNavController().navigate(
+//                    .actionNavigationTransactionsToNavigationEditTransaction(bundle)
+//                )
+            }
+        )
+        recyclerView.adapter = transactionListAdapter
+
+        recyclerView.layoutManager = LinearLayoutManager(requireContext(),
+            RecyclerView.VERTICAL,
+            false
+            )
+        return root
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        viewModel.fetchData()
+        viewModel.transactionList?.observe(viewLifecycleOwner){
+            it?.let {
+                transactionListAdapter.submitList(it)
+            }
+        }
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        _binding = null
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/ui/transactions/TransactionsViewModel.kt b/app/src/main/java/pbd/tubes/exe_android/ui/transactions/TransactionsViewModel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c46d829a673d6ad06f3606878f12e223077fa77a
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/ui/transactions/TransactionsViewModel.kt
@@ -0,0 +1,48 @@
+package pbd.tubes.exe_android.ui.transactions
+
+import android.util.Log
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.asLiveData
+import androidx.lifecycle.viewModelScope
+import androidx.lifecycle.viewmodel.CreationExtras
+import kotlinx.coroutines.launch
+import pbd.tubes.exe_android.BondomanApp
+import pbd.tubes.exe_android.data.database.TransactionsRepository
+import pbd.tubes.exe_android.models.Transaction
+
+class TransactionsViewModel(private val transactionsRepository: TransactionsRepository) : ViewModel() {
+    var transactionList: LiveData<List<Transaction>>? = null
+    //TODO(handling empty list)
+    //TODO(fragment(or MainActivity?) margin(or padding?) is too low)
+    fun fetchData(){
+        viewModelScope.launch {
+            transactionList = transactionsRepository.getAllTransactionsByRecentStream().asLiveData()
+        }
+    }
+    fun deleteItem(transaction: Transaction){
+        viewModelScope.launch {
+            Log.d("MyApp", "Delete is sent to repo")
+            transactionsRepository.deleteTransaction(transaction)
+            fetchData()
+        }
+    }
+    companion object {
+        val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
+            @Suppress("UNCHECKED_CAST")
+            override fun <T : ViewModel> create(
+                modelClass: Class<T>,
+                extras: CreationExtras
+            )
+                    : T {
+                val application = checkNotNull(extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY])
+//                val savedStateHandle = extras.createSavedStateHandle()
+                return TransactionsViewModel(
+                    (application as BondomanApp).container.transactionRepository,
+//                    savedStateHandle
+                ) as T
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/ui/transactions/component/TransactionListAdapter.kt b/app/src/main/java/pbd/tubes/exe_android/ui/transactions/component/TransactionListAdapter.kt
new file mode 100644
index 0000000000000000000000000000000000000000..4009fd60a87a119b0151d558fb754302294e9d0e
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/ui/transactions/component/TransactionListAdapter.kt
@@ -0,0 +1,28 @@
+package pbd.tubes.exe_android.ui.transactions.component
+
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import pbd.tubes.exe_android.models.Transaction
+
+class TransactionListAdapter(
+    private val onDeleteClickListener: (Transaction) -> Unit,
+    private val onEditClickListener: (Transaction) -> Unit
+) : ListAdapter<Transaction, TransactionViewHolder> (TransactionComparator()) {
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TransactionViewHolder {
+        return TransactionViewHolder.create(parent,onDeleteClickListener,onEditClickListener)
+    }
+    override fun onBindViewHolder(holder: TransactionViewHolder, position: Int) {
+        val current = getItem(position)
+        holder.bind(current)
+    }
+    class TransactionComparator : DiffUtil.ItemCallback<Transaction>() {
+        override fun areItemsTheSame(oldItem: Transaction, newItem: Transaction): Boolean {
+            return oldItem === newItem
+        }
+
+        override fun areContentsTheSame(oldItem: Transaction, newItem: Transaction): Boolean {
+            return oldItem.id == newItem.id
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/pbd/tubes/exe_android/ui/transactions/component/TransactionViewHolder.kt b/app/src/main/java/pbd/tubes/exe_android/ui/transactions/component/TransactionViewHolder.kt
new file mode 100644
index 0000000000000000000000000000000000000000..83a6b373158b265ddf7712ed65bc2f185bf60157
--- /dev/null
+++ b/app/src/main/java/pbd/tubes/exe_android/ui/transactions/component/TransactionViewHolder.kt
@@ -0,0 +1,56 @@
+package pbd.tubes.exe_android.ui.transactions.component
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageButton
+import android.widget.TextView
+import androidx.core.content.ContextCompat
+import androidx.recyclerview.widget.RecyclerView
+import pbd.tubes.exe_android.R
+import pbd.tubes.exe_android.models.Transaction
+import pbd.tubes.exe_android.models.TransactionCategory
+
+class TransactionViewHolder(
+    itemView : View,
+    private val deleteListener: ((Transaction) -> Unit),
+    private val editListener: ((Transaction) -> Unit)
+): RecyclerView.ViewHolder(itemView){
+    //bindings
+    private val item_name: TextView = itemView.findViewById(R.id.item_name)
+    private val item_nominal: TextView = itemView.findViewById(R.id.item_nominal)
+    private val item_category: TextView = itemView.findViewById(R.id.item_category)
+    private val item_location: TextView = itemView.findViewById(R.id.item_location)
+    private val item_date: TextView = itemView.findViewById(R.id.item_date)
+    private var item_delete: ImageButton = itemView.findViewById(R.id.delete_card)
+    private var item_edit: ImageButton = itemView.findViewById(R.id.edit_card)
+    fun bind (transaction: Transaction) {
+        item_name.text = transaction.name
+        item_nominal.text = String.format("IDR %.2f", transaction.nominal)
+        item_category.text = transaction.category.toString().lowercase().replaceFirstChar { it.uppercase() }
+        if (transaction.category == TransactionCategory.PEMASUKAN) {
+            item_category.background = ContextCompat.getDrawable(itemView.context, R.drawable.pemasukan_tag)
+        } else if (transaction.category == TransactionCategory.PEMBELIAN) {
+            item_category.background = ContextCompat.getDrawable(itemView.context, R.drawable.pembelian_tag)
+        }
+        item_location.text = transaction.lokasi
+        item_date.text = transaction.date.toString()
+        item_delete.setOnClickListener{
+            deleteListener.invoke(transaction)
+        }
+        item_edit.setOnClickListener{
+            editListener.invoke(transaction)
+        }
+    }
+
+    companion object {
+        fun create(parent: ViewGroup,
+                   deleteListener: ((Transaction) -> Unit),
+                   editListener: ((Transaction) -> Unit))
+        : TransactionViewHolder {
+            val view: View = LayoutInflater.from(parent.context)
+                .inflate(R.layout.transaction_list_item, parent, false)
+            return TransactionViewHolder(view, deleteListener, editListener)
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/bondoman.png b/app/src/main/res/drawable/bondoman.png
new file mode 100644
index 0000000000000000000000000000000000000000..3a5f5a2b020249fbbc169ca7a52f7010bd2514de
Binary files /dev/null and b/app/src/main/res/drawable/bondoman.png differ
diff --git a/app/src/main/res/drawable/camera_button.xml b/app/src/main/res/drawable/camera_button.xml
new file mode 100644
index 0000000000000000000000000000000000000000..65e1847247697741490cc45d8f4f2ca80383bccc
--- /dev/null
+++ b/app/src/main/res/drawable/camera_button.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_pressed="true">
+        <shape
+            android:shape="oval"
+            android:tint="@color/gray_200"/>
+    </item>
+    <item>
+        <shape
+            android:shape="oval"
+            android:tint="@color/white"/>
+    </item>
+</selector>
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_add_24.xml b/app/src/main/res/drawable/ic_add_24.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9f83b8fbe793b0df45feab2a5a20f9af548bb136
--- /dev/null
+++ b/app/src/main/res/drawable/ic_add_24.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
+      
+    <path android:fillColor="@android:color/white" android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
+    
+</vector>
diff --git a/app/src/main/res/drawable/ic_dashboard_black_24dp.xml b/app/src/main/res/drawable/ic_dashboard_black_24dp.xml
deleted file mode 100644
index 46fc8deec32bb06fedfe594815e568e3a6bf48b9..0000000000000000000000000000000000000000
--- a/app/src/main/res/drawable/ic_dashboard_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M3,13h8L11,3L3,3v10zM3,21h8v-6L3,15v6zM13,21h8L21,11h-8v10zM13,3v6h8L21,3h-8z" />
-</vector>
diff --git a/app/src/main/res/drawable/ic_delete_24.xml b/app/src/main/res/drawable/ic_delete_24.xml
new file mode 100644
index 0000000000000000000000000000000000000000..883bcaaccbd72b62d9cfc1dd61a0b38c873346a6
--- /dev/null
+++ b/app/src/main/res/drawable/ic_delete_24.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
+      
+    <path android:fillColor="@android:color/white" android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
+    
+</vector>
diff --git a/app/src/main/res/drawable/ic_document_scanner_24.xml b/app/src/main/res/drawable/ic_document_scanner_24.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c32313b67dcd717800df1b3b0e24c31b32d5dbcd
--- /dev/null
+++ b/app/src/main/res/drawable/ic_document_scanner_24.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp" android:tint="#000000"
+    android:viewportHeight="24" android:viewportWidth="24"
+    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@android:color/white" android:pathData="M7,3H4v3H2V1h5V3zM22,6V1h-5v2h3v3H22zM7,21H4v-3H2v5h5V21zM20,18v3h-3v2h5v-5H20zM19,18c0,1.1 -0.9,2 -2,2H7c-1.1,0 -2,-0.9 -2,-2V6c0,-1.1 0.9,-2 2,-2h10c1.1,0 2,0.9 2,2V18zM15,8H9v2h6V8zM15,11H9v2h6V11zM15,14H9v2h6V14z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_done_24.xml b/app/src/main/res/drawable/ic_done_24.xml
new file mode 100644
index 0000000000000000000000000000000000000000..adb7ae67ef8b5bfd3d948d55db5258bf39b5b0de
--- /dev/null
+++ b/app/src/main/res/drawable/ic_done_24.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
+      
+    <path android:fillColor="@android:color/white" android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"/>
+    
+</vector>
diff --git a/app/src/main/res/drawable/ic_edit_24.xml b/app/src/main/res/drawable/ic_edit_24.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3c53db7ec89d67f61bef8a823938a980e5668736
--- /dev/null
+++ b/app/src/main/res/drawable/ic_edit_24.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
+      
+    <path android:fillColor="@android:color/white" android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
+    
+</vector>
diff --git a/app/src/main/res/drawable/ic_history_24.xml b/app/src/main/res/drawable/ic_history_24.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a4749974d0724eb9fa724bb5d111444fd54e2912
--- /dev/null
+++ b/app/src/main/res/drawable/ic_history_24.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp" android:tint="#000000"
+    android:viewportHeight="24" android:viewportWidth="24"
+    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@android:color/white" android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_home_black_24dp.xml b/app/src/main/res/drawable/ic_home_black_24dp.xml
deleted file mode 100644
index f8bb0b55633d3e41228e9e285149af5f1d839d08..0000000000000000000000000000000000000000
--- a/app/src/main/res/drawable/ic_home_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z" />
-</vector>
diff --git a/app/src/main/res/drawable/ic_image_24.xml b/app/src/main/res/drawable/ic_image_24.xml
new file mode 100644
index 0000000000000000000000000000000000000000..fd890cefb4f1544083da72c67c5b5989ace76aa0
--- /dev/null
+++ b/app/src/main/res/drawable/ic_image_24.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
+      
+    <path android:fillColor="@android:color/white" android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
+    
+</vector>
diff --git a/app/src/main/res/drawable/ic_location_pin_24.xml b/app/src/main/res/drawable/ic_location_pin_24.xml
new file mode 100644
index 0000000000000000000000000000000000000000..dd88a43b8d529d640038f9cddf459e5e3c0cd40e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_location_pin_24.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
+      
+    <path android:fillColor="@android:color/white" android:pathData="M12,2L12,2C8.13,2 5,5.13 5,9c0,1.74 0.5,3.37 1.41,4.84c0.95,1.54 2.2,2.86 3.16,4.4c0.47,0.75 0.81,1.45 1.17,2.26C11,21.05 11.21,22 12,22h0c0.79,0 1,-0.95 1.25,-1.5c0.37,-0.81 0.7,-1.51 1.17,-2.26c0.96,-1.53 2.21,-2.85 3.16,-4.4C18.5,12.37 19,10.74 19,9C19,5.13 15.87,2 12,2zM12,11.75c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5s2.5,1.12 2.5,2.5S13.38,11.75 12,11.75z"/>
+    
+</vector>
diff --git a/app/src/main/res/drawable/ic_notifications_black_24dp.xml b/app/src/main/res/drawable/ic_notifications_black_24dp.xml
deleted file mode 100644
index 78b75c39b55bc3cbbc18bfe7d9165fb3da7d03ff..0000000000000000000000000000000000000000
--- a/app/src/main/res/drawable/ic_notifications_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
-    <path
-        android:fillColor="#FF000000"
-        android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.89,2 2,2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2z" />
-</vector>
diff --git a/app/src/main/res/drawable/ic_pie_chart_24.xml b/app/src/main/res/drawable/ic_pie_chart_24.xml
new file mode 100644
index 0000000000000000000000000000000000000000..db98ba9c6866aa4d9fc389916cfd974c6dc9d5b7
--- /dev/null
+++ b/app/src/main/res/drawable/ic_pie_chart_24.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp" android:tint="#000000"
+    android:viewportHeight="24" android:viewportWidth="24"
+    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@android:color/white" android:pathData="M11,2v20c-5.07,-0.5 -9,-4.79 -9,-10s3.93,-9.5 9,-10zM13.03,2v8.99L22,10.99c-0.47,-4.74 -4.24,-8.52 -8.97,-8.99zM13.03,13.01L13.03,22c4.74,-0.47 8.5,-4.25 8.97,-8.99h-8.97z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_receipt_24.xml b/app/src/main/res/drawable/ic_receipt_24.xml
new file mode 100644
index 0000000000000000000000000000000000000000..181d1d03be83a35b691f5f08ea6f688e3ba84542
--- /dev/null
+++ b/app/src/main/res/drawable/ic_receipt_24.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp" android:tint="#000000"
+    android:viewportHeight="24" android:viewportWidth="24"
+    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@android:color/white" android:pathData="M18,17L6,17v-2h12v2zM18,13L6,13v-2h12v2zM18,9L6,9L6,7h12v2zM3,22l1.5,-1.5L6,22l1.5,-1.5L9,22l1.5,-1.5L12,22l1.5,-1.5L15,22l1.5,-1.5L18,22l1.5,-1.5L21,22L21,2l-1.5,1.5L18,2l-1.5,1.5L15,2l-1.5,1.5L12,2l-1.5,1.5L9,2 7.5,3.5 6,2 4.5,3.5 3,2v20z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_redo_24.xml b/app/src/main/res/drawable/ic_redo_24.xml
new file mode 100644
index 0000000000000000000000000000000000000000..39b0396e956a09945ec2e43475816b9ade25a6e6
--- /dev/null
+++ b/app/src/main/res/drawable/ic_redo_24.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
+      
+    <path android:fillColor="@android:color/white" android:pathData="M18.4,10.6C16.55,8.99 14.15,8 11.5,8c-4.65,0 -8.58,3.03 -9.96,7.22L3.9,16c1.05,-3.19 4.05,-5.5 7.6,-5.5 1.95,0 3.73,0.72 5.12,1.88L13,16h9V7l-3.6,3.6z"/>
+    
+</vector>
diff --git a/app/src/main/res/drawable/ic_settings_24.xml b/app/src/main/res/drawable/ic_settings_24.xml
new file mode 100644
index 0000000000000000000000000000000000000000..298a5a1ff28c4ce6a5591285f569dc6c827e1b15
--- /dev/null
+++ b/app/src/main/res/drawable/ic_settings_24.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp" android:tint="#000000"
+    android:viewportHeight="24" android:viewportWidth="24"
+    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@android:color/white" android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z"/>
+</vector>
diff --git a/app/src/main/res/drawable/pemasukan_tag.xml b/app/src/main/res/drawable/pemasukan_tag.xml
new file mode 100644
index 0000000000000000000000000000000000000000..db06b5c47666f863115521a0b8cff6bb34838656
--- /dev/null
+++ b/app/src/main/res/drawable/pemasukan_tag.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="@color/pemasukan_color" />
+
+    <corners android:radius="10dip" />
+
+    <stroke
+        android:width="2dp"
+        android:color="@android:color/transparent"/>
+</shape>
\ No newline at end of file
diff --git a/app/src/main/res/drawable/pembelian_tag.xml b/app/src/main/res/drawable/pembelian_tag.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8a61363eb96217c6ca3fbdbda4b5c0a180382187
--- /dev/null
+++ b/app/src/main/res/drawable/pembelian_tag.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="@color/pembelian_color" />
+
+    <corners android:radius="10dip" />
+
+    <stroke
+        android:width="2dp"
+        android:color="@android:color/transparent"/>
+</shape>
\ No newline at end of file
diff --git a/app/src/main/res/drawable/rounded_rectangle.xml b/app/src/main/res/drawable/rounded_rectangle.xml
new file mode 100644
index 0000000000000000000000000000000000000000..0c78b28c835b9eaa417801eb6872cc3bc006561e
--- /dev/null
+++ b/app/src/main/res/drawable/rounded_rectangle.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="@android:color/white" />
+
+    <corners android:radius="10dip" />
+
+    <stroke
+        android:width="2dp"
+        android:color="@android:color/transparent"/>
+</shape>
\ No newline at end of file
diff --git a/app/src/main/res/layout-land/activity_main.xml b/app/src/main/res/layout-land/activity_main.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7c8cd7b4fc15eb3a7d61eeb1f00d0533dd7d87f3
--- /dev/null
+++ b/app/src/main/res/layout-land/activity_main.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    >
+
+    <com.google.android.material.navigationrail.NavigationRailView
+        android:id="@+id/nav_view_rail"
+        android:layout_width="wrap_content"
+        android:layout_height="0dp"
+        android:layout_marginStart="0dp"
+        android:layout_marginEnd="0dp"
+        android:background="?android:attr/windowBackground"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:menu="@menu/bottom_nav_menu"
+        />
+
+    <com.google.android.material.floatingactionbutton.FloatingActionButton
+        android:id="@+id/add_fab"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@drawable/ic_add_24"
+        android:layout_marginBottom="16dp"
+        android:layout_marginEnd="16dp"
+        android:contentDescription="@string/transaction_operations_fab_text"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        android:layout_gravity="bottom|end"
+        android:backgroundTint="@color/design_default_color_primary"
+        />
+
+    <fragment
+        android:id="@+id/nav_host_fragment_activity_main"
+        android:name="androidx.navigation.fragment.NavHostFragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        app:defaultNavHost="true"
+        app:layout_constraintBottom_toTopOf="parent"
+        app:layout_constraintLeft_toRightOf="@id/nav_view_rail"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:navGraph="@navigation/mobile_navigation" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c1a502cd24001ad452010b8205e553d41e13c5ab
--- /dev/null
+++ b/app/src/main/res/layout/activity_login.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:gravity="center"
+    tools:ignore="HardcodedText">
+
+    <TextView
+        android:id="@+id/loginTextView"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Login"
+        android:textSize="20sp"
+        android:layout_centerHorizontal="true"
+        android:layout_marginBottom="16dp"/>
+
+    <EditText
+        android:id="@+id/emailEditText"
+        android:layout_centerHorizontal="true"
+        android:minWidth="300dp"
+        style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/loginTextView"
+        android:layout_margin="16dp"
+        android:layout_marginStart="16dp"
+        android:layout_marginEnd="16dp"
+        android:hint="Email"
+        android:inputType="textEmailAddress"
+        android:minHeight="48dp"
+        />
+
+    <EditText
+        android:id="@+id/passwordEditText"
+        style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
+        android:layout_width="wrap_content"
+        android:minWidth="300dp"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/emailEditText"
+        android:layout_margin="16dp"
+        android:layout_marginStart="16dp"
+        android:layout_marginEnd="16dp"
+        android:hint="Password"
+        android:inputType="textPassword"
+        android:minHeight="48dp"
+        android:layout_centerHorizontal="true"
+        />
+
+    <Button
+        android:id="@+id/loginButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Login"
+        android:layout_below="@id/passwordEditText"
+        android:layout_centerHorizontal="true"
+        android:layout_marginTop="24dp"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 06ea6cae22113f243efe317f984f7742418737e8..e9ae331a67257a17eec216858e353724ddb53024 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -4,25 +4,37 @@
     android:id="@+id/container"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:paddingTop="?attr/actionBarSize">
+    >
 
     <com.google.android.material.bottomnavigation.BottomNavigationView
         android:id="@+id/nav_view"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:layout_marginStart="0dp"
-        android:layout_marginEnd="0dp"
         android:background="?android:attr/windowBackground"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintLeft_toLeftOf="parent"
         app:layout_constraintRight_toRightOf="parent"
         app:menu="@menu/bottom_nav_menu" />
 
+    <com.google.android.material.floatingactionbutton.FloatingActionButton
+        android:id="@+id/add_fab"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@drawable/ic_add_24"
+        android:layout_marginBottom="16dp"
+        android:layout_marginEnd="16dp"
+        android:contentDescription="@string/transaction_operations_fab_text"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/nav_view"
+        android:layout_gravity="bottom|end"
+        android:backgroundTint="@color/design_default_color_primary"
+        />
+
     <fragment
         android:id="@+id/nav_host_fragment_activity_main"
         android:name="androidx.navigation.fragment.NavHostFragment"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
+        android:layout_height="0dp"
         app:defaultNavHost="true"
         app:layout_constraintBottom_toTopOf="@id/nav_view"
         app:layout_constraintLeft_toLeftOf="parent"
diff --git a/app/src/main/res/layout/fragment_add_transaction.xml b/app/src/main/res/layout/fragment_add_transaction.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f7b8a418e836dc5e76dac32923bc34b713487009
--- /dev/null
+++ b/app/src/main/res/layout/fragment_add_transaction.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".ui.transactions.AddTransactionFragment"
+    tools:ignore="HardcodedText">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:layout_gravity="center_horizontal"
+        >
+
+        <com.google.android.material.textfield.TextInputLayout
+            android:id="@+id/transactionNameInputLayout"
+            style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="8dp"
+            android:minWidth="300dp"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintHorizontal_bias="0.5"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent">
+
+            <com.google.android.material.textfield.TextInputEditText
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:hint="@string/transaction_name_text"
+                android:singleLine="true"
+                android:textColorHint="@android:color/transparent" />
+        </com.google.android.material.textfield.TextInputLayout>
+
+        <com.google.android.material.textfield.TextInputLayout
+            android:id="@+id/transactionNominalInputLayout"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="32dp"
+            android:minWidth="300dp"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintHorizontal_bias="0.5"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/transactionNameInputLayout"
+            style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
+
+            <com.google.android.material.textfield.TextInputEditText
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:hint="@string/transaction_nominal_text"
+                android:inputType="number"
+                android:singleLine="true"
+                android:textColorHint="@android:color/transparent"
+                android:value="" />
+        </com.google.android.material.textfield.TextInputLayout>
+
+        <TextView
+            android:id="@+id/transaction_category"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="32dp"
+            android:text="@string/transaction_category"
+            app:layout_constraintBottom_toTopOf="@+id/categoryRadioGroup"
+            app:layout_constraintEnd_toEndOf="@+id/categoryRadioGroup"
+            app:layout_constraintHorizontal_bias="0.0"
+            app:layout_constraintStart_toStartOf="@+id/categoryRadioGroup"
+            app:layout_constraintTop_toBottomOf="@+id/transactionNominalInputLayout" />
+
+        <RadioGroup
+            android:id="@+id/categoryRadioGroup"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:minWidth="300dp"
+            android:orientation="horizontal"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/transaction_category">
+
+            <RadioButton
+                android:id="@+id/radioPembelian"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:minWidth="150dp"
+                android:text="Pembelian" />
+
+            <RadioButton
+                android:id="@+id/radioPemasukan"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:minWidth="150dp"
+                android:text="Pemasukan" />
+        </RadioGroup>
+
+        <com.google.android.material.textfield.TextInputLayout
+            android:id="@+id/transactionLocationInputLayout"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="32dp"
+            android:minWidth="300dp"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/categoryRadioGroup"
+            style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
+
+            <com.google.android.material.textfield.TextInputEditText
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:hint="@string/transaction_location"
+                android:singleLine="true"
+                android:textColorHint="@android:color/transparent" />
+
+        </com.google.android.material.textfield.TextInputLayout>
+
+        <Button
+            android:id="@+id/submit_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="24dp"
+            android:text="Submit"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/transactionLocationInputLayout" />
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</ScrollView>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_chart.xml b/app/src/main/res/layout/fragment_chart.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9ddcc4a3d325147bb1b17f5962d949971734130d
--- /dev/null
+++ b/app/src/main/res/layout/fragment_chart.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".ui.chart.ChartFragment"
+    >
+
+<!--    <TextView-->
+<!--        android:layout_width="wrap_content"-->
+<!--        android:layout_height="wrap_content"-->
+<!--        app:layout_constraintTop_toTopOf="parent"-->
+<!--        app:layout_constraintStart_toStartOf="parent"-->
+<!--        app:layout_constraintEnd_toEndOf="parent"-->
+<!--        android:layout_marginTop="32dp"-->
+<!--        app:layout_constraintHorizontal_bias="0.15"-->
+<!--        />-->
+
+    <com.github.mikephil.charting.charts.PieChart
+        android:id="@+id/pieChart"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:minWidth="300dp"
+        android:minHeight="300dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_bias="0.3" />
+<!--        app:layout_constraintBottom_toBottomOf="parent"-->
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_dashboard.xml b/app/src/main/res/layout/fragment_dashboard.xml
deleted file mode 100644
index 166ab0e9e603c1f230a7b9514d293b963ab2309e..0000000000000000000000000000000000000000
--- a/app/src/main/res/layout/fragment_dashboard.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    tools:context=".ui.dashboard.DashboardFragment">
-
-    <TextView
-        android:id="@+id/text_dashboard"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="8dp"
-        android:layout_marginTop="8dp"
-        android:layout_marginEnd="8dp"
-        android:textAlignment="center"
-        android:textSize="20sp"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent" />
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_edit_transaction.xml b/app/src/main/res/layout/fragment_edit_transaction.xml
new file mode 100644
index 0000000000000000000000000000000000000000..84371848763e2f0357372c8440303bf72ada6637
--- /dev/null
+++ b/app/src/main/res/layout/fragment_edit_transaction.xml
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:ignore="HardcodedText"
+    tools:context=".ui.transactions.EditTransactionFragment"
+    >
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+    <com.google.android.material.textfield.TextInputLayout
+        android:id="@+id/transactionEditNameInputLayout"
+        style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:minWidth="300dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="0.5"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent">
+
+        <com.google.android.material.textfield.TextInputEditText
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:hint="@string/transaction_name_text"
+            android:singleLine="true"
+            android:textColorHint="@android:color/transparent" />
+    </com.google.android.material.textfield.TextInputLayout>
+
+    <com.google.android.material.textfield.TextInputLayout
+        android:id="@+id/transactionEditNominalInputLayout"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="32dp"
+        android:minWidth="300dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="0.5"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/transactionEditNameInputLayout"
+        style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
+
+        <com.google.android.material.textfield.TextInputEditText
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:hint="@string/transaction_nominal_text"
+            android:inputType="number"
+            android:singleLine="true"
+            android:textColorHint="@android:color/transparent"
+            android:value="" />
+    </com.google.android.material.textfield.TextInputLayout>
+
+        <TextView
+            android:id="@+id/transactionEdit_category"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="32dp"
+            android:text="@string/transaction_category"
+            app:layout_constraintBottom_toTopOf="@+id/categoryRadioGroup"
+            app:layout_constraintEnd_toEndOf="@+id/categoryRadioGroup"
+            app:layout_constraintHorizontal_bias="0.0"
+            app:layout_constraintStart_toStartOf="@+id/categoryRadioGroup"
+            app:layout_constraintTop_toBottomOf="@+id/transactionEditDateInputLayout" />
+
+        <RadioGroup
+            android:id="@+id/categoryRadioGroup"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:minWidth="300dp"
+            android:orientation="horizontal"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/transactionEdit_category"
+            android:clickable="false"
+            >
+
+            <RadioButton
+                android:id="@+id/radioPembelian"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:minWidth="150dp"
+                android:text="Pembelian"
+                android:enabled="false"
+                android:clickable="false"
+                />
+
+            <RadioButton
+                android:id="@+id/radioPemasukan"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:minWidth="150dp"
+                android:text="Pemasukan"
+                android:enabled="false"
+                android:clickable="false"
+                />
+        </RadioGroup>
+
+    <com.google.android.material.textfield.TextInputLayout
+        android:id="@+id/transactionEditLocationInputLayout"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="32dp"
+        android:minWidth="300dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/transactionEditNominalInputLayout"
+        style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
+
+        <com.google.android.material.textfield.TextInputEditText
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:hint="@string/transaction_location"
+            android:singleLine="true"
+            android:textColorHint="@android:color/transparent"
+            />
+
+    </com.google.android.material.textfield.TextInputLayout>
+
+    <com.google.android.material.textfield.TextInputLayout
+        android:id="@+id/transactionEditDateInputLayout"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="32dp"
+        android:minWidth="300dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/transactionEditLocationInputLayout"
+        style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
+
+        <com.google.android.material.textfield.TextInputEditText
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:hint="Tanggal"
+            android:singleLine="true"
+            android:textColorHint="@android:color/transparent"
+            android:enabled="false"
+            android:clickable="false"
+            />
+
+    </com.google.android.material.textfield.TextInputLayout>
+
+
+    <Button
+        android:id="@+id/submit_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:text="Submit"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/categoryRadioGroup" />
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</ScrollView>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_notifications.xml b/app/src/main/res/layout/fragment_notifications.xml
deleted file mode 100644
index d41793572bb3b8347ec4bced74b7bd4a43bed5d4..0000000000000000000000000000000000000000
--- a/app/src/main/res/layout/fragment_notifications.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    tools:context=".ui.notifications.NotificationsFragment">
-
-    <TextView
-        android:id="@+id/text_notifications"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="8dp"
-        android:layout_marginTop="8dp"
-        android:layout_marginEnd="8dp"
-        android:textAlignment="center"
-        android:textSize="20sp"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent" />
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_scan.xml b/app/src/main/res/layout/fragment_scan.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7fd955ab425a71c042772c8f244d77f301d7dc5d
--- /dev/null
+++ b/app/src/main/res/layout/fragment_scan.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/scan_fragment"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".ui.scan.ScanFragment">
+
+    <androidx.camera.view.PreviewView
+        android:id="@+id/viewFinder"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+    </androidx.camera.view.PreviewView>
+
+    <ImageButton
+        android:id="@+id/image_capture_button"
+        android:layout_width="110dp"
+        android:layout_height="110dp"
+        android:layout_marginBottom="50dp"
+        android:background="@drawable/camera_button"
+        android:contentDescription="@string/take_picture"
+        android:elevation="1dp"
+        android:text="@string/take_photo"
+        android:scaleType="fitXY"
+        android:padding="24dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <ImageButton
+        android:id="@+id/pick_image_button"
+        android:layout_width="60dp"
+        android:layout_height="60dp"
+        android:layout_marginEnd="32dp"
+        android:background="@drawable/rounded_rectangle"
+        android:contentDescription="@string/pick_picture"
+        android:elevation="1dp"
+        android:minWidth="50dp"
+        android:minHeight="50dp"
+        android:src="@drawable/ic_image_24"
+        app:layout_constraintBottom_toBottomOf="@+id/image_capture_button"
+        app:layout_constraintEnd_toStartOf="@+id/image_capture_button"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="@+id/image_capture_button" />
+
+    <ImageView
+        android:id="@+id/image_preview"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_marginBottom="50dp"
+        android:contentDescription="@string/photo_preview"
+        android:elevation="2dp"
+        android:visibility="visible"
+        app:layout_constraintBottom_toTopOf="@+id/image_capture_button"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..86f2a2300f62f6e866cfcb80082a1f66526f7028
--- /dev/null
+++ b/app/src/main/res/layout/fragment_settings.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".ui.settings.SettingsFragment"
+    tools:ignore="HardcodedText"
+    android:id="@+id/fragment_settings"
+    >
+
+    <ToggleButton
+        android:id="@+id/save_button"
+        android:textColor="@android:color/black"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:textOn="Simpan daftar transaksi"
+        android:textOff="Simpan daftar transaksi"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+    <TextView
+        android:id="@+id/format_daftar_transaksi"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Format daftar transaksi:"
+        app:layout_constraintTop_toBottomOf="@id/save_button"
+        app:layout_constraintLeft_toLeftOf="@id/save_button"
+        app:layout_constraintRight_toRightOf="@id/save_button"
+        app:layout_constraintHorizontal_bias="0.15"
+        android:visibility="gone"
+        />
+    <RadioGroup
+        android:id="@+id/radio_format"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="start"
+        app:layout_constraintLeft_toLeftOf="@id/format_daftar_transaksi"
+        app:layout_constraintRight_toRightOf="@id/format_daftar_transaksi"
+        app:layout_constraintTop_toBottomOf="@id/format_daftar_transaksi"
+        android:orientation="vertical"
+        app:layout_constraintHorizontal_bias="0.15"
+        android:visibility="gone"
+        >
+        <RadioButton
+            android:id="@+id/xls_choice"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text=".xls"/>
+        <RadioButton
+            android:id="@+id/xlsx_choice"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text=".xlsx"/>
+    </RadioGroup>
+    <Button
+        android:id="@+id/confirm_save_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:minWidth="300dp"
+        style="@style/Widget.Material3.Button"
+        android:textAppearance="@style/TextAppearance.AppCompat.Medium"
+        android:text="SAVE"
+        app:layout_constraintLeft_toLeftOf="@id/radio_format"
+        app:layout_constraintTop_toBottomOf="@id/radio_format"
+        android:visibility="gone"
+        />
+
+    <Button
+        android:id="@+id/send_button"
+        style="@style/Widget.Material3.Button"
+        android:textAppearance="@style/TextAppearance.AppCompat.Medium"
+        android:backgroundTint="@color/gray_200"
+        android:textColor="@android:color/black"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:text="Kirim daftar transaksi"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="@+id/save_button"
+        app:layout_constraintTop_toBottomOf="@+id/confirm_save_button" />
+
+    <Button
+        android:id="@+id/randomize_button"
+        style="@style/Widget.Material3.Button"
+        android:textAppearance="@style/TextAppearance.AppCompat.Medium"
+        android:backgroundTint="@color/gray_200"
+        android:textColor="@android:color/black"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:text="Randomize transaksi"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="@+id/save_button"
+        app:layout_constraintTop_toBottomOf="@+id/send_button" />
+
+    <Button
+        android:id="@+id/log_out_button"
+        style="@style/Widget.Material3.Button"
+        android:textAppearance="@style/TextAppearance.AppCompat.Medium"
+        android:textColor="@color/design_default_color_error"
+        android:backgroundTint="@color/gray_200"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:text="Keluar"
+        app:layout_constraintEnd_toEndOf="@+id/send_button"
+        app:layout_constraintStart_toStartOf="@+id/send_button"
+        app:layout_constraintTop_toBottomOf="@+id/randomize_button" />
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_transactions.xml
similarity index 55%
rename from app/src/main/res/layout/fragment_home.xml
rename to app/src/main/res/layout/fragment_transactions.xml
index f3d9b08ffe6101e25c77c5fae7e28bb5dfa11fbd..de5fa8741f69cb7b0d845b6b211b47cc231bc7a5 100644
--- a/app/src/main/res/layout/fragment_home.xml
+++ b/app/src/main/res/layout/fragment_transactions.xml
@@ -4,19 +4,15 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    tools:context=".ui.home.HomeFragment">
+    tools:context=".ui.transactions.TransactionsFragment">
 
-    <TextView
-        android:id="@+id/text_home"
-        android:layout_width="match_parent"
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/transaction_recycler"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginStart="8dp"
-        android:layout_marginTop="8dp"
-        android:layout_marginEnd="8dp"
-        android:textAlignment="center"
-        android:textSize="20sp"
-        app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent" />
+        app:layout_constraintTop_toTopOf="parent"
+
+        />
 </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/transaction_list_item.xml b/app/src/main/res/layout/transaction_list_item.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4bf99a3422e51192593ddc386415cc59778e2463
--- /dev/null
+++ b/app/src/main/res/layout/transaction_list_item.xml
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    app:cardCornerRadius="16dp"
+    android:minWidth="300dp"
+    app:cardBackgroundColor="@color/gray_200"
+    android:minHeight="100dp"
+    android:layout_marginBottom="32dp"
+    tools:ignore="HardcodedText"
+    >
+
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_gravity="center|start"
+        android:minHeight="100dp"
+        android:minWidth="300dp"
+        >
+
+        <FrameLayout
+            android:id="@+id/item_detail"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:minHeight="100dp"
+            android:minWidth="300dp"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent">
+
+            <TextView
+                android:id="@+id/item_name"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center|start"
+                android:layout_margin="8dp"
+                android:text="@string/transaction_name_text"
+                android:textColor="@color/black"
+                />
+
+            <TextView
+                android:id="@+id/item_nominal"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="bottom|start"
+                android:layout_margin="8dp"
+                android:textColor="@color/black"
+                android:text="@string/transaction_nominal_text" />
+
+            <TextView
+                android:id="@+id/item_category"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="end|top"
+                android:layout_margin="8dp"
+                android:textColor="@color/black"
+                android:text="@string/transaction_category"
+                android:background="@drawable/pembelian_tag"
+                android:paddingHorizontal="8dp"
+                />
+
+            <TextView
+                android:id="@+id/item_location"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="bottom|end"
+                android:layout_margin="8dp"
+                android:textColor="@color/black"
+                android:text="@string/transaction_location"
+                app:drawableEndCompat="@drawable/ic_location_pin_24" />
+
+            <TextView
+                android:id="@+id/item_date"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="top|start"
+                android:layout_margin="8dp"
+                android:textColor="@color/black"
+                android:text="DD-MM-YYYY"
+                android:contentDescription="Tanggal transaksi"
+                />
+        </FrameLayout>
+
+        <LinearLayout
+            android:layout_width="300dp"
+            android:layout_height="match_parent"
+            android:minWidth="300dp"
+            android:minHeight="48dp"
+            android:orientation="horizontal"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/item_detail"
+            >
+            <!--TODO(Edit Transaction has bug)-->
+            <ImageButton
+                android:id="@+id/edit_card"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:background="@color/cardview_light_background"
+                android:src="@drawable/ic_edit_24"
+                android:layout_weight="1"
+                android:contentDescription="Edit Button"
+                android:visibility="gone"
+                />
+
+            <ImageButton
+                android:id="@+id/delete_card"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:background="@color/design_default_color_error"
+                android:src="@drawable/ic_delete_24"
+                android:layout_weight="1"
+                android:contentDescription="Delete"
+                />
+        </LinearLayout>
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</androidx.cardview.widget.CardView>
\ No newline at end of file
diff --git a/app/src/main/res/menu/bottom_nav_menu.xml b/app/src/main/res/menu/bottom_nav_menu.xml
index fb6d040b9a026ddaa05df0e530ab6f95e6c1f999..0301c9a023044586da5a44233bc711bb37a896c0 100644
--- a/app/src/main/res/menu/bottom_nav_menu.xml
+++ b/app/src/main/res/menu/bottom_nav_menu.xml
@@ -1,19 +1,26 @@
 <?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/bottom_nav_menu"
+    >
 
     <item
-        android:id="@+id/navigation_home"
-        android:icon="@drawable/ic_home_black_24dp"
-        android:title="@string/title_home" />
+        android:id="@+id/navigation_transactions"
+        android:icon="@drawable/ic_history_24"
+        android:title="@string/title_transactions" />
 
     <item
-        android:id="@+id/navigation_dashboard"
-        android:icon="@drawable/ic_dashboard_black_24dp"
-        android:title="@string/title_dashboard" />
+        android:id="@+id/navigation_scan"
+        android:icon="@drawable/ic_document_scanner_24"
+        android:title="@string/title_scan" />
 
     <item
-        android:id="@+id/navigation_notifications"
-        android:icon="@drawable/ic_notifications_black_24dp"
-        android:title="@string/title_notifications" />
+        android:id="@+id/navigation_chart"
+        android:icon="@drawable/ic_pie_chart_24"
+        android:title="@string/title_chart" />
+
+    <item
+        android:id="@+id/navigation_settings"
+        android:icon="@drawable/ic_settings_24"
+        android:title="@string/title_settings" />
 
 </menu>
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000000000000000000000000000000000000..036d09bc5fd523323794379703c4a111d1e28a04
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@color/ic_launcher_background"/>
+    <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon>
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000000000000000000000000000000000000..036d09bc5fd523323794379703c4a111d1e28a04
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@color/ic_launcher_background"/>
+    <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon>
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher.xml b/app/src/main/res/mipmap-anydpi/ic_launcher.xml
deleted file mode 100644
index 6f3b755bf50c6b03d8714a9c6184705e6a08389f..0000000000000000000000000000000000000000
--- a/app/src/main/res/mipmap-anydpi/ic_launcher.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
-    <background android:drawable="@drawable/ic_launcher_background" />
-    <foreground android:drawable="@drawable/ic_launcher_foreground" />
-    <monochrome android:drawable="@drawable/ic_launcher_foreground" />
-</adaptive-icon>
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml
deleted file mode 100644
index 6f3b755bf50c6b03d8714a9c6184705e6a08389f..0000000000000000000000000000000000000000
--- a/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
-    <background android:drawable="@drawable/ic_launcher_background" />
-    <foreground android:drawable="@drawable/ic_launcher_foreground" />
-    <monochrome android:drawable="@drawable/ic_launcher_foreground" />
-</adaptive-icon>
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp
index c209e78ecd372343283f4157dcfd918ec5165bb3..f22c3481d4d691a0c26c21b8b585e698a9fe52ac 100644
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000000000000000000000000000000000000..56fb8839d687f93864e949a8c37a2278bf4d66cd
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
index b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9..5c72ddf2acabc8a36ff32fd0b3b8e67bb1b93362 100644
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp
index 4f0f1d64e58ba64d180ce43ee13bf9a17835fbca..401b99d79487e7ad9d9450798111939d77ee760f 100644
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000000000000000000000000000000000000..e06d47490c67ee4085e9db59d73f008710f45fe9
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
index 62b611da081676d42f6c3f78a2c91e7bcedddedb..3dbfa9c1d93d242d30a0edf8fc7063dfd24e44d8 100644
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
index 948a3070fe34c611c42c0d3ad3013a0dce358be0..53c0d9f00162879c829849d7aef81f38f7b3b57f 100644
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000000000000000000000000000000000000..cf188de4455de8411b6127cc763b1b35502941a9
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
index 1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f..05f54dcf5511219ca711466bff7c9d4f63fbe4b5 100644
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
index 28d4b77f9f036a47549d47db79c16788749dca10..adbcbe750040240265c1925dd2242a29326ab5c2 100644
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000000000000000000000000000000000000..da9290fd2813cb347e95ea8a6df273d452509575
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
index 9287f5083623b375139afb391af71cc533a7dd37..de6a44735807a982738b81b35117858f4cb09665 100644
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
index aa7d6427e6fa1074b79ccd52ef67ac15c5637e85..3a1c2ae225d592f5fcf19cd48b2e25a7a5af319f 100644
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000000000000000000000000000000000000..13e269998bf3797fb0d89bb0532018c95b03568c
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
index 9126ae37cbc3587421d6889eadd1d91fbf1994d4..e01325c62a8e5dad4a99087556dae2c679e49cf0 100644
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml
index d46c7d0305e3ace935b47639d1020ff6ec3bd0c9..4e5707724034876f13c52831face882febd239a6 100644
--- a/app/src/main/res/navigation/mobile_navigation.xml
+++ b/app/src/main/res/navigation/mobile_navigation.xml
@@ -3,23 +3,55 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/mobile_navigation"
-    app:startDestination="@+id/navigation_home">
+    app:startDestination="@+id/navigation_transactions"
+    app:defaultNavHost="true"
+    app:navGraph="@navigation/mobile_navigation"
+    >
 
     <fragment
-        android:id="@+id/navigation_home"
-        android:name="pbd.tubes.exe_android.ui.home.HomeFragment"
-        android:label="@string/title_home"
-        tools:layout="@layout/fragment_home" />
+        android:id="@+id/navigation_transactions"
+        android:name="pbd.tubes.exe_android.ui.transactions.TransactionsFragment"
+        android:label="@string/title_transactions"
+        tools:layout="@layout/fragment_transactions"
+        />
+<!--    >-->
+<!--        <action-->
+<!--            android:id="@+id/action_navigation_transactions_to_navigation_edit_transaction"-->
+<!--            app:destination="@id/navigation_edit_transaction">-->
+<!--            <argument-->
+<!--                android:name="transaction"-->
+<!--                app:argType="pbd.tubes.exe_android.models.Transaction" />-->
+<!--        </action>-->
+<!--    </fragment>-->
 
     <fragment
-        android:id="@+id/navigation_dashboard"
-        android:name="pbd.tubes.exe_android.ui.dashboard.DashboardFragment"
-        android:label="@string/title_dashboard"
-        tools:layout="@layout/fragment_dashboard" />
+        android:id="@+id/navigation_scan"
+        android:name="pbd.tubes.exe_android.ui.scan.ScanFragment"
+        android:label="@string/title_scan"
+        tools:layout="@layout/fragment_scan" />
 
     <fragment
-        android:id="@+id/navigation_notifications"
-        android:name="pbd.tubes.exe_android.ui.notifications.NotificationsFragment"
-        android:label="@string/title_notifications"
-        tools:layout="@layout/fragment_notifications" />
+        android:id="@+id/navigation_chart"
+        android:name="pbd.tubes.exe_android.ui.chart.ChartFragment"
+        android:label="@string/title_chart"
+        tools:layout="@layout/fragment_chart" />
+
+    <fragment
+        android:id="@+id/navigation_settings"
+        android:name="pbd.tubes.exe_android.ui.settings.SettingsFragment"
+        android:label="@string/title_settings"
+        tools:layout="@layout/fragment_settings" />
+
+    <fragment
+        android:id="@+id/navigation_add_transaction"
+        android:name="pbd.tubes.exe_android.ui.transactions.AddTransactionFragment"
+        android:label="Add Transaction"
+        tools:layout="@layout/fragment_add_transaction"/>
+
+    <fragment
+        android:id="@+id/navigation_edit_transaction"
+        android:name="pbd.tubes.exe_android.ui.transactions.EditTransactionFragment"
+        android:label="Edit Transaction"
+        tools:layout="@layout/fragment_edit_transaction"/>
+
 </navigation>
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index f8c6127d327620c93d2b2d00342a68e97b98a48d..995bd8411199754be09f57ce924b31ac4ab86580 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -7,4 +7,12 @@
     <color name="teal_700">#FF018786</color>
     <color name="black">#FF000000</color>
     <color name="white">#FFFFFFFF</color>
+    <color name="light_blue_400">#FF29B6F6</color>
+    <color name="light_blue_600">#FF039BE5</color>
+    <color name="gray_200">#D7D7D7</color>
+    <color name="gray_400">#D7D7D7</color>
+    <color name="gray_600">#FF757575</color>
+
+    <color name="pemasukan_color">#8EF2A4</color>
+    <color name="pembelian_color">#ED561C</color>
 </resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c5d5899fdf0a1b144bf341b29e0c66ba50bbcedd
--- /dev/null
+++ b/app/src/main/res/values/ic_launcher_background.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="ic_launcher_background">#FFFFFF</color>
+</resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d27be3d5f42ed2c3cf59202e24a55e73bfaf8b71..2142b2877ac9f18fc59aa115acf9bcce18c8b566 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,6 +1,19 @@
 <resources>
-    <string name="app_name">exe_android</string>
+    <string name="app_name">BondoMan</string>
     <string name="title_home">Home</string>
     <string name="title_dashboard">Dashboard</string>
     <string name="title_notifications">Notifications</string>
+    <string name="title_transactions">Transaksi</string>
+    <string name="title_scan">Scan</string>
+    <string name="title_chart">Graf</string>
+    <string name="title_settings">Pengaturan</string>
+    <string name="transaction_operations_fab_text">Add Transaction</string>
+    <string name="transaction_name_text">Nama Transaksi</string>
+    <string name="transaction_nominal_text">Nominal</string>
+    <string name="transaction_category">Kategori</string>
+    <string name="transaction_location">Lokasi</string>
+    <string name="take_photo">ambil foto</string>
+    <string name="take_picture">Take Picture</string>
+    <string name="pick_picture">Pick picture</string>
+    <string name="photo_preview">Photo Preview</string>
 </resources>
\ No newline at end of file
diff --git a/app/src/main/res/xml/file_paths.xml b/app/src/main/res/xml/file_paths.xml
new file mode 100644
index 0000000000000000000000000000000000000000..791000e4925e4f5d623dbdb27894db7d2adc2057
--- /dev/null
+++ b/app/src/main/res/xml/file_paths.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<paths xmlns:android="http://schemas.android.com/apk/res/android">
+    <external-path
+        name="external_files"
+        path="." />
+</paths>
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index 53f4a67287fcc572dab6ad907bddc40aa4efbfa6..161d31be377ef9dd4d613966992c0d454119c37c 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,5 +1,6 @@
 // Top-level build file where you can add configuration options common to all sub-projects/modules.
 plugins {
-    id("com.android.application") version "8.2.2" apply false
-    id("org.jetbrains.kotlin.android") version "1.9.22" apply false
+    id("com.android.application") version "8.3.1" apply false
+    id("org.jetbrains.kotlin.android") version "1.9.23" apply false
+    id("com.google.devtools.ksp") version "1.9.23-1.0.19" apply false
 }
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 3c5031eb7d63f785752b1914cc8692a453d1cc63..c22da5736c4a0ff537d51c41ac7ab53331239a81 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -20,4 +20,5 @@ kotlin.code.style=official
 # Enables namespacing of each library's R class so that its R class includes only the
 # resources declared in the library itself and none from the library's dependencies,
 # thereby reducing the size of the R class for that library
-android.nonTransitiveRClass=true
\ No newline at end of file
+android.nonTransitiveRClass=true
+android.enableJetifier=true
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index fe507cd1912cec38b4353bfa295972bd604e5bc5..82b76d38a7b3a9b11350d46d6abb00edebc58a61 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
 #Wed Mar 13 19:06:54 WIB 2024
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/settings.gradle.kts b/settings.gradle.kts
index e214361957eb3a305e9e620ed7ed5fc278c804c4..4670cb7836ef8495b9d3f25388f04fd0c67ae697 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -3,6 +3,7 @@ pluginManagement {
         google()
         mavenCentral()
         gradlePluginPortal()
+        maven ("https://jitpack.io")
     }
 }
 dependencyResolutionManagement {
@@ -10,6 +11,7 @@ dependencyResolutionManagement {
     repositories {
         google()
         mavenCentral()
+        maven ("https://jitpack.io")
     }
 }