diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 7d760a5e0cb43c980fe1c071a02760b50157b7ba..d789f27f4ee080f95e17fcb7ef62dd4643b44b67 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -1,6 +1,7 @@
 plugins {
     id("com.android.application")
     id("org.jetbrains.kotlin.android")
+    id("com.google.devtools.ksp")
 }
 
 android {
@@ -36,6 +37,8 @@ android {
 }
 
 dependencies {
+    implementation("com.google.android.gms:play-services-location:21.2.0")
+    val roomVersion = "2.6.1"
 
     implementation("androidx.core:core-ktx:1.8.0")
     implementation("androidx.appcompat:appcompat:1.5.1")
@@ -64,6 +67,9 @@ dependencies {
     implementation("com.squareup.retrofit2:converter-moshi:2.9.0")
     implementation("androidx.preference:preference:1.2.1")
 
+    implementation("androidx.room:room-ktx:$roomVersion")
+    ksp("androidx.room:room-compiler:$roomVersion")
+
     testImplementation("junit:junit:4.13.2")
     androidTestImplementation("androidx.test.ext:junit:1.1.5")
     androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f1ec3ca3962e81c27a5cfc2d2f70cc2cf38411e2..534fd6f8b27b87cad3d3cdacdd66d550b8c2ed55 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,9 +2,12 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools">
 
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.INTERNET" />
 
     <application
+        android:name=".ui.transactions.TransactionsApplication"
         android:allowBackup="true"
         android:dataExtractionRules="@xml/data_extraction_rules"
         android:fullBackupContent="@xml/backup_rules"
diff --git a/app/src/main/java/com/example/bondoyap/MainActivity.kt b/app/src/main/java/com/example/bondoyap/MainActivity.kt
index bb0a8bfb7024ffb846255ee25e928a26a381d598..45bd03acce96fb18184ec41c63a83e7262d0af71 100644
--- a/app/src/main/java/com/example/bondoyap/MainActivity.kt
+++ b/app/src/main/java/com/example/bondoyap/MainActivity.kt
@@ -49,6 +49,11 @@ class MainActivity : AppCompatActivity() {
 
         navView.setupWithNavController(navController)
     }
+
+    override fun onSupportNavigateUp(): Boolean {
+        val navController = findNavController(R.id.nav_host_fragment_activity_main)
+        return navController.navigateUp() || super.onSupportNavigateUp()
+    }
     private fun isLoggedIn(): Boolean {
         return sharedPreferences.getBoolean("isLoggedIn", false)
     }
diff --git a/app/src/main/java/com/example/bondoyap/ui/login/LoginActivity.kt b/app/src/main/java/com/example/bondoyap/ui/login/LoginActivity.kt
index e64b43e08ad5bb67e76fc8892e9ef61387d0e9ee..e21b23e4fa0c8c171cb0f1e56f67f635e6dae6b2 100644
--- a/app/src/main/java/com/example/bondoyap/ui/login/LoginActivity.kt
+++ b/app/src/main/java/com/example/bondoyap/ui/login/LoginActivity.kt
@@ -49,6 +49,14 @@ class LoginActivity : AppCompatActivity() {
             loginViewModel = ViewModelProvider(this, factory)[LoginViewModel::class.java]
 
             // skip login
+            loginViewModel.login(
+                "13521xxx@std.stei.itb.ac.id",
+                "password_13521xxx"
+            )
+            if (isLoggedIn()) {
+                navigateToMainActivity()
+                finish()
+            }
 
             loginViewModel.loginFormState.observe(this@LoginActivity, Observer {
                 val loginState = it ?: return@Observer
diff --git a/app/src/main/java/com/example/bondoyap/ui/transactions/AddTransactionsFragment.kt b/app/src/main/java/com/example/bondoyap/ui/transactions/AddTransactionsFragment.kt
new file mode 100644
index 0000000000000000000000000000000000000000..88d64ef36a124fb2e7a944540da9f4ca8a2a2dd2
--- /dev/null
+++ b/app/src/main/java/com/example/bondoyap/ui/transactions/AddTransactionsFragment.kt
@@ -0,0 +1,137 @@
+package com.example.bondoyap.ui.transactions
+
+import android.R
+import android.content.pm.PackageManager
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ArrayAdapter
+import android.widget.Toast
+import androidx.core.app.ActivityCompat
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import com.example.bondoyap.databinding.FragmentAddTransactionsBinding
+import com.example.bondoyap.ui.transactions.data.Transactions
+import com.google.android.gms.location.FusedLocationProviderClient
+import com.google.android.gms.location.LocationServices
+import java.text.SimpleDateFormat
+import java.util.Date
+import java.util.Locale
+
+class AddTransactionsFragment : Fragment() {
+
+    private var _binding: FragmentAddTransactionsBinding? = null
+
+    // This property is only valid between onCreateView and
+    // onDestroyView.
+    private val binding get() = _binding!!
+
+    private val transactionsViewModel: TransactionsViewModel by viewModels {
+        TransactionsViewModelFactory((requireContext().applicationContext as TransactionsApplication).repository)
+    }
+
+    private lateinit var fusedLocationProviderClient: FusedLocationProviderClient
+    private lateinit var latitude: String
+    private lateinit var longitude: String
+
+    override fun onCreateView(
+            inflater: LayoutInflater,
+            container: ViewGroup?,
+            savedInstanceState: Bundle?
+    ): View {
+        _binding = FragmentAddTransactionsBinding.inflate(inflater, container, false)
+
+        val pemasukan = "Pemasukan"
+        val pengeluaran = "Pengeluaran"
+
+        val categories = arrayOf(pemasukan, pengeluaran)
+        val adapter = ArrayAdapter(requireContext(), R.layout.simple_spinner_item, categories)
+
+        adapter.setDropDownViewResource(R.layout.simple_spinner_dropdown_item)
+        binding.spinnerKategori.adapter = adapter
+
+        fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(requireContext())
+
+        if (
+            ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.ACCESS_FINE_LOCATION)
+            != PackageManager.PERMISSION_GRANTED
+            && ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.ACCESS_COARSE_LOCATION)
+            != PackageManager.PERMISSION_GRANTED
+        ) {
+            ActivityCompat.requestPermissions(requireActivity(),
+                arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION), 100
+            )
+        }
+
+        binding.buttonSimpan.setOnClickListener {
+            val isPemasukan: Boolean = when (binding.spinnerKategori.selectedItem.toString()) {
+                pemasukan -> true
+                else -> false
+            }
+
+            val dateFormat = SimpleDateFormat("dd/MM/yyyy", Locale.getDefault())
+            val currentDate = dateFormat.format(Date())
+
+            val judul = if (binding.editTextJudul.text.toString().trim().isNotEmpty()) {
+                binding.editTextJudul.text.toString()
+            } else {
+                "Untitled"
+            }
+
+            val nominal = if (binding.editTextNominal.text.toString().trim().isNotEmpty()) {
+                binding.editTextNominal.text.toString().toDouble()
+            } else {
+                0.0
+            }
+
+            val location = fusedLocationProviderClient.lastLocation
+            location.addOnSuccessListener {
+                if(it != null) {
+                    latitude = it.latitude.toString()
+                    longitude = it.longitude.toString()
+
+                    val transaction: Transactions = Transactions(
+                        judul = judul,
+                        nominal = nominal,
+                        isPemasukan = isPemasukan,
+                        tanggal = currentDate,
+                        longitude = longitude,
+                        latitude = latitude
+                    )
+                    transactionsViewModel.upsert(transaction)
+                }
+            }
+
+            if(
+                ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.ACCESS_FINE_LOCATION)
+                != PackageManager.PERMISSION_GRANTED
+                && ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.ACCESS_COARSE_LOCATION)
+                != PackageManager.PERMISSION_GRANTED
+            ) {
+                val transaction: Transactions = Transactions(
+                    judul = judul,
+                    nominal = nominal,
+                    isPemasukan = isPemasukan,
+                    tanggal = currentDate,
+                    longitude = "",
+                    latitude = ""
+                )
+                transactionsViewModel.upsert(transaction)
+            }
+
+            Toast.makeText(requireContext(), "Transaksi berhasil disimpan", Toast.LENGTH_SHORT).show()
+
+            binding.editTextJudul.text.clear()
+            binding.editTextNominal.text.clear()
+            binding.spinnerKategori.setSelection(0)
+        }
+
+        return binding.root
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        _binding = null
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bondoyap/ui/transactions/EditTransactionsFragment.kt b/app/src/main/java/com/example/bondoyap/ui/transactions/EditTransactionsFragment.kt
new file mode 100644
index 0000000000000000000000000000000000000000..f72aaec034e554588d337ae3b8a2d80d30291e71
--- /dev/null
+++ b/app/src/main/java/com/example/bondoyap/ui/transactions/EditTransactionsFragment.kt
@@ -0,0 +1,246 @@
+package com.example.bondoyap.ui.transactions
+
+import android.R
+import android.app.AlertDialog
+import android.content.pm.PackageManager
+import android.location.Address
+import android.location.Geocoder
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.text.SpannableStringBuilder
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ArrayAdapter
+import android.widget.Toast
+import androidx.core.app.ActivityCompat
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import androidx.navigation.fragment.findNavController
+import com.example.bondoyap.databinding.FragmentEditTransactionsBinding
+import com.example.bondoyap.ui.transactions.data.Transactions
+import com.google.android.gms.location.FusedLocationProviderClient
+import com.google.android.gms.location.LocationServices
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers.Main
+import kotlinx.coroutines.launch
+import java.util.Locale
+
+class EditTransactionsFragment : Fragment() {
+
+    private var _binding: FragmentEditTransactionsBinding? = null
+
+    // This property is only valid between onCreateView and
+    // onDestroyView.
+    private val binding get() = _binding!!
+
+    private val transactionsViewModel: TransactionsViewModel by viewModels {
+        TransactionsViewModelFactory((requireContext().applicationContext as TransactionsApplication).repository)
+    }
+
+    private lateinit var fusedLocationProviderClient: FusedLocationProviderClient
+
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View {
+        _binding = FragmentEditTransactionsBinding.inflate(inflater, container, false)
+
+        var tanggal: String = ""
+        var latitude = ""
+        var longitude = ""
+
+        val pemasukan = "Pemasukan"
+        val pengeluaran = "Pengeluaran"
+
+        val categories = arrayOf(pemasukan, pengeluaran)
+        val adapter = ArrayAdapter(requireContext(), R.layout.simple_spinner_item, categories)
+
+        adapter.setDropDownViewResource(R.layout.simple_spinner_dropdown_item)
+        binding.spinnerKategori.adapter = adapter
+
+        binding.spinnerKategori.isEnabled = false
+        binding.editTextLokasi.isEnabled = false
+
+        val transactionId: Int = arguments?.getInt("transaction_id") ?: -1
+
+        CoroutineScope(Main).launch {
+
+            val originalTransaction: Transactions = transactionsViewModel.get(transactionId)
+
+            binding.editTextJudul.text = SpannableStringBuilder(originalTransaction.judul)
+            binding.editTextNominal.text = SpannableStringBuilder(originalTransaction.nominal.toBigDecimal().toString())
+
+            if (originalTransaction.isPemasukan) {
+                binding.spinnerKategori.setSelection(categories.indexOf(pemasukan))
+            } else {
+                binding.spinnerKategori.setSelection(categories.indexOf(pengeluaran))
+            }
+
+            tanggal = originalTransaction.tanggal
+            longitude = originalTransaction.longitude
+            latitude = originalTransaction.latitude
+
+            if (originalTransaction.longitude.isEmpty() || originalTransaction.latitude.isEmpty()) {
+                binding.editTextLokasi.text = SpannableStringBuilder("Unavailable")
+            } else {
+                val addresses: List<Address> =
+                    Geocoder(requireContext(), Locale.getDefault()).getFromLocation(
+                        originalTransaction.latitude.toDouble(),
+                        originalTransaction.longitude.toDouble(),
+                        1
+                    ) ?: emptyList()
+                if (addresses.isNotEmpty()) {
+                    val locationName = addresses[0].getAddressLine(0)
+                    Handler(Looper.getMainLooper()).post {
+                        binding.editTextLokasi.text = SpannableStringBuilder(locationName)
+                    }
+                }
+            }
+        }
+
+        fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(requireContext())
+
+        if (
+            ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.ACCESS_FINE_LOCATION)
+            != PackageManager.PERMISSION_GRANTED
+            && ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.ACCESS_COARSE_LOCATION)
+            != PackageManager.PERMISSION_GRANTED
+        ) {
+            ActivityCompat.requestPermissions(requireActivity(),
+                arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION), 100
+            )
+        }
+
+        binding.checkboxUpdateLokasi.setOnCheckedChangeListener { _, isChecked ->
+            if (isChecked) {
+                if (
+                    ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.ACCESS_FINE_LOCATION)
+                    != PackageManager.PERMISSION_GRANTED
+                    && ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.ACCESS_COARSE_LOCATION)
+                    != PackageManager.PERMISSION_GRANTED
+                ) {
+                    ActivityCompat.requestPermissions(requireActivity(),
+                        arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION), 100
+                    )
+                    binding.checkboxUpdateLokasi.isChecked = false
+                }
+            }
+        }
+
+        binding.buttonUpdate.setOnClickListener {
+            val isPemasukan: Boolean = when (binding.spinnerKategori.selectedItem.toString()) {
+                pemasukan -> true
+                else -> false
+            }
+
+            val judul = if (binding.editTextJudul.text.toString().trim().isNotEmpty()) {
+                binding.editTextJudul.text.toString()
+            } else {
+                "Untitled"
+            }
+
+            val nominal = if (binding.editTextNominal.text.toString().trim().isNotEmpty()) {
+                binding.editTextNominal.text.toString().toDouble()
+            } else {
+                0.0
+            }
+
+            val transaction: Transactions = Transactions(
+                judul = judul,
+                nominal = nominal,
+                isPemasukan = isPemasukan,
+                tanggal = tanggal,
+                longitude = longitude,
+                latitude = latitude,
+                id = transactionId
+            )
+
+            if (binding.checkboxUpdateLokasi.isChecked) {
+                val location = fusedLocationProviderClient.lastLocation
+                location.addOnSuccessListener {
+                    if (it != null) {
+                        latitude = it.latitude.toString()
+                        longitude = it.longitude.toString()
+
+                        transaction.latitude = latitude
+                        transaction.longitude = longitude
+                    }
+                }
+            }
+
+            showConfirmationDialog("Update", "Apakah Anda yakin ingin memperbarui transaksi ini?") {
+                transactionsViewModel.upsert(transaction)
+
+                binding.editTextJudul.text = SpannableStringBuilder(transaction.judul)
+                binding.editTextNominal.text =
+                    SpannableStringBuilder(transaction.nominal.toBigDecimal().toString())
+
+                if (transaction.longitude.isEmpty() || transaction.latitude.isEmpty()) {
+                    binding.editTextLokasi.text = SpannableStringBuilder("Unavailable")
+                } else {
+                    val addresses: List<Address> =
+                        Geocoder(requireContext(), Locale.getDefault()).getFromLocation(
+                            transaction.latitude.toDouble(),
+                            transaction.longitude.toDouble(),
+                            1
+                        ) ?: emptyList()
+                    if (addresses.isNotEmpty()) {
+                        val locationName = addresses[0].getAddressLine(0)
+                        Handler(Looper.getMainLooper()).post {
+                            binding.editTextLokasi.text = SpannableStringBuilder(locationName)
+                        }
+                    }
+                }
+
+                if (transaction.isPemasukan) {
+                    binding.spinnerKategori.setSelection(categories.indexOf(pemasukan))
+                } else {
+                    binding.spinnerKategori.setSelection(categories.indexOf(pengeluaran))
+                }
+                Toast.makeText(requireContext(), "Transaksi berhasil diperbarui", Toast.LENGTH_SHORT).show()
+            }
+
+
+        }
+
+        binding.buttonHapus.setOnClickListener {
+            val transaction: Transactions = Transactions(
+                judul = "",
+                nominal = 0.0,
+                isPemasukan = false,
+                tanggal = "",
+                id = transactionId
+            )
+
+            showConfirmationDialog("Hapus", "Apakah Anda yakin ingin menghapus transaksi ini?") {
+                transactionsViewModel.delete(transaction)
+                findNavController().navigate(com.example.bondoyap.R.id.navigation_transactions)
+                Toast.makeText(requireContext(), "Transaksi berhasil dihapus", Toast.LENGTH_SHORT).show()
+            }
+        }
+
+        return binding.root
+    }
+
+    private fun showConfirmationDialog(title: String, message: String, action: () -> Unit) {
+        val builder = AlertDialog.Builder(requireContext())
+        builder.setTitle(title)
+        builder.setMessage(message)
+        builder.setPositiveButton("Ya") { _, _ ->
+            action.invoke()
+        }
+        builder.setNegativeButton("Tidak") { dialog, _ ->
+            dialog.dismiss()
+        }
+        val dialog = builder.create()
+        dialog.show()
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        _binding = null
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsApplication.kt b/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsApplication.kt
new file mode 100644
index 0000000000000000000000000000000000000000..0634b8129e6625beb80b829d449e8f6219f26988
--- /dev/null
+++ b/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsApplication.kt
@@ -0,0 +1,10 @@
+package com.example.bondoyap.ui.transactions
+
+import android.app.Application
+import com.example.bondoyap.ui.transactions.data.TransactionsRepository
+import com.example.bondoyap.ui.transactions.data.TransactionsRoomDatabase
+
+class TransactionsApplication: Application() {
+    val database by lazy { TransactionsRoomDatabase.getDatabase(this) }
+    val repository by lazy { TransactionsRepository(database.transactionsDao()) }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsFragment.kt b/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsFragment.kt
index b8cbc5f6ddde72493ea3dc5d9e81c53015e6557e..036cd83e1724a379928591b30b629adc6a195603 100644
--- a/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsFragment.kt
+++ b/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsFragment.kt
@@ -1,42 +1,60 @@
 package com.example.bondoyap.ui.transactions
 
+import android.content.pm.PackageManager
 import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
-import android.widget.TextView
+import androidx.core.app.ActivityCompat
 import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.Observer
 import androidx.lifecycle.ViewModelProvider
+import androidx.navigation.fragment.findNavController
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.example.bondoyap.R
 import com.example.bondoyap.databinding.FragmentTransactionsBinding
 
-class TransactionsFragment : Fragment() {
-
+class TransactionsFragment: Fragment() {
     private var _binding: FragmentTransactionsBinding? = null
 
     // This property is only valid between onCreateView and
     // onDestroyView.
     private val binding get() = _binding!!
 
+    private val transactionsViewModel: TransactionsViewModel by viewModels {
+        TransactionsViewModelFactory((requireContext().applicationContext as TransactionsApplication).repository)
+    }
+
+    private val requestcode: Int = 1
+
     override fun onCreateView(
-            inflater: LayoutInflater,
-            container: ViewGroup?,
-            savedInstanceState: Bundle?
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
     ): View {
-        val transactionsViewModel =
-                ViewModelProvider(this).get(TransactionsViewModel::class.java)
-
         _binding = FragmentTransactionsBinding.inflate(inflater, container, false)
-        val root: View = binding.root
 
-        val textView: TextView = binding.textTransactions
-        transactionsViewModel.text.observe(viewLifecycleOwner) {
-            textView.text = it
+        val recyclerView = binding.root.findViewById<RecyclerView>(R.id.recyclerViewTransactions)
+        val adapter = TransactionsListAdapter(context = requireContext())
+        recyclerView.adapter = adapter
+        recyclerView.layoutManager = LinearLayoutManager(requireContext())
+
+        transactionsViewModel.allTransactions.observe(viewLifecycleOwner, Observer { transactions ->
+            transactions?.let { adapter.submitList(it) }
+        })
+
+        binding.buttonAddTransaction.setOnClickListener {
+            findNavController().navigate(R.id.navigation_add_transactions)
         }
-        return root
+
+        return binding.root
     }
 
     override fun onDestroyView() {
         super.onDestroyView()
         _binding = null
     }
+
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsListAdapter.kt b/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsListAdapter.kt
new file mode 100644
index 0000000000000000000000000000000000000000..6c5dce13bf504632c030d4a57aa4b6fc47b3991e
--- /dev/null
+++ b/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsListAdapter.kt
@@ -0,0 +1,147 @@
+package com.example.bondoyap.ui.transactions
+
+import android.content.Context
+import android.content.Intent
+import android.location.Address
+import android.location.Geocoder
+import android.net.Uri
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import android.widget.Toast
+import androidx.cardview.widget.CardView
+import androidx.core.content.ContextCompat.startActivity
+import androidx.navigation.Navigation
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
+import com.example.bondoyap.R
+import com.example.bondoyap.ui.transactions.data.Transactions
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import java.io.IOException
+import java.util.Locale
+
+class TransactionsListAdapter(private val context: Context) :
+    ListAdapter<Transactions, TransactionsViewHolder>(TransactionsDiffCallback()) {
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TransactionsViewHolder {
+        return TransactionsViewHolder.create(parent, context)
+    }
+
+    override fun onBindViewHolder(holder: TransactionsViewHolder, position: Int) {
+        val currentTransaction = getItem(position)
+        holder.bind(currentTransaction)
+    }
+
+}
+
+class TransactionsViewHolder(itemView: View, private val context: Context) : RecyclerView.ViewHolder(itemView) {
+    private val cardView: CardView = itemView.findViewById(R.id.cardViewTransaction)
+    private val transactionTitle: TextView = itemView.findViewById(R.id.transactionTitle)
+    private val transactionAmount: TextView = itemView.findViewById(R.id.transactionAmount)
+    private val transactionCategory: TextView = itemView.findViewById(R.id.transactionCategory)
+    private val transactionDate: TextView = itemView.findViewById(R.id.transactionDate)
+    private val transactionLocation: TextView = itemView.findViewById(R.id.transactionLocation)
+    private val geocoder: Geocoder = Geocoder(context, Locale.getDefault())
+
+    fun bind(transaction: Transactions) {
+        val maxAmountLength = 12
+        val maxTitleLength = 16
+        val maxLocationLength = 9
+
+        val amountText = if (transaction.nominal.toBigDecimal().toString().length > maxAmountLength) {
+            "IDR " + transaction.nominal.toBigDecimal().toString().substring(0, maxAmountLength) + "..."
+        } else {
+            "IDR " + transaction.nominal.toBigDecimal().toString()
+        }
+
+        val titleText = if (transaction.judul.length > maxTitleLength) {
+            transaction.judul.substring(0, maxTitleLength) + "..."
+        } else {
+            transaction.judul
+        }
+
+        transactionTitle.text = titleText
+        transactionAmount.text = amountText
+        transactionCategory.text = when (transaction.isPemasukan) {
+            true -> "Pemasukan"
+            else -> "Pengeluaran"
+        }
+
+        transactionDate.text = transaction.tanggal
+
+        if (transaction.latitude.isNotEmpty() && transaction.longitude.isNotEmpty()) {
+            CoroutineScope(Dispatchers.IO).launch {
+                try {
+                    val addresses: List<Address> = geocoder.getFromLocation(
+                        transaction.latitude.toDouble(),
+                        transaction.longitude.toDouble(),
+                        1
+                    ) ?: emptyList()
+                    if (addresses.isNotEmpty()) {
+                        val locationName = addresses[0].getAddressLine(0)
+                        Handler(Looper.getMainLooper()).post {
+                            transactionLocation.text = if (locationName.length > maxLocationLength) {
+                                locationName.substring(0, maxLocationLength) + "..."
+                            } else {
+                                locationName
+                            }
+                        }
+                    }
+                } catch (e: IOException) {
+                    e.printStackTrace()
+                }
+            }
+        } else {
+            transactionLocation.text = "Unavailable"
+        }
+
+        transactionLocation.setOnClickListener {
+            if (transactionLocation.text != "Unavailable") {
+                val mapUri = Uri.parse("https://maps.google.com/maps/search/?api=1&query=${transaction.latitude},${transaction.longitude}")
+                val intent = Intent(Intent.ACTION_VIEW, mapUri)
+                intent.setPackage("com.google.android.apps.maps")
+                if (intent.resolveActivity(context.packageManager) != null) {
+                    startActivity(context, intent, null)
+                } else {
+                    val webIntent = Intent(Intent.ACTION_VIEW, Uri.parse("https://www.google.com/maps/search/?api=1&query=${transaction.latitude},${transaction.longitude}"))
+                    if (webIntent.resolveActivity(context.packageManager) != null) {
+                        startActivity(context, webIntent, null)
+                    } else {
+                        Toast.makeText(context, "Tidak ada app yang dapat menghandle maps", Toast.LENGTH_SHORT).show()
+                    }
+                }
+            }
+        }
+
+        cardView.setOnClickListener {
+            val bundle: Bundle = Bundle()
+            bundle.putInt("transaction_id", transaction.id)
+            Navigation.findNavController(itemView).navigate(R.id.navigation_edit_transactions, bundle)
+        }
+    }
+
+    companion object {
+        fun create(parent: ViewGroup, context: Context): TransactionsViewHolder {
+            val view: View = LayoutInflater.from(parent.context)
+                .inflate(R.layout.recyclerview_transactions, parent, false)
+            return TransactionsViewHolder(view, context)
+        }
+    }
+}
+
+class TransactionsDiffCallback : DiffUtil.ItemCallback<Transactions>() {
+    override fun areItemsTheSame(oldItem: Transactions, newItem: Transactions): Boolean {
+        return oldItem.id == newItem.id
+    }
+
+    override fun areContentsTheSame(oldItem: Transactions, newItem: Transactions): Boolean {
+        return oldItem == newItem
+    }
+}
diff --git a/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsViewModel.kt b/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsViewModel.kt
index 1a790c6f8709dc3c2f42d091b860cde6389501c3..012172e8ce21819fb181c307aa74520ef627be80 100644
--- a/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsViewModel.kt
+++ b/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsViewModel.kt
@@ -1,13 +1,41 @@
 package com.example.bondoyap.ui.transactions
 
 import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.asLiveData
+import androidx.lifecycle.viewModelScope
+import com.example.bondoyap.ui.transactions.data.Transactions
+import com.example.bondoyap.ui.transactions.data.TransactionsRepository
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.async
+import kotlinx.coroutines.launch
 
-class TransactionsViewModel : ViewModel() {
+class TransactionsViewModel(
+    private val repository: TransactionsRepository
+): ViewModel() {
+    val allTransactions: LiveData<List<Transactions>> = repository.allTransactions.asLiveData()
 
-    private val _text = MutableLiveData<String>().apply {
-        value = "This is Transactions Fragment"
+    fun upsert(transactions: Transactions) = viewModelScope.launch {
+        repository.upsert(transactions)
+    }
+    fun delete(transactions: Transactions) = viewModelScope.launch {
+        repository.delete(transactions)
+    }
+    suspend fun get(transactionId: Int?): Transactions {
+        val deferred: Deferred<Transactions> = viewModelScope.async {
+            repository.get(transactionId)
+        }
+        return deferred.await()
+    }
+}
+
+class TransactionsViewModelFactory(private val repository: TransactionsRepository): ViewModelProvider.Factory {
+    override fun<T: ViewModel> create(modelClass: Class<T>): T {
+        if (modelClass.isAssignableFrom(TransactionsViewModel::class.java)) {
+            @Suppress("UNCHECKED_CAST")
+            return TransactionsViewModel(repository) as T
+        }
+        throw IllegalArgumentException("Unknown ViewModel class")
     }
-    val text: LiveData<String> = _text
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bondoyap/ui/transactions/data/Transactions.kt b/app/src/main/java/com/example/bondoyap/ui/transactions/data/Transactions.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a5bddbd9edcc1d9bc8323ad08c4ae69d6949e37d
--- /dev/null
+++ b/app/src/main/java/com/example/bondoyap/ui/transactions/data/Transactions.kt
@@ -0,0 +1,30 @@
+package com.example.bondoyap.ui.transactions.data
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+@Entity(tableName = "transactions")
+data class Transactions(
+    @ColumnInfo(name = "judul")
+    val judul: String,
+
+    @ColumnInfo(name = "nominal")
+    val nominal: Double,
+
+    @ColumnInfo(name = "is_pemasukan")
+    val isPemasukan: Boolean,
+
+    @ColumnInfo(name = "tanggal")
+    val tanggal: String,
+
+    @ColumnInfo(name = "longitude")
+    var longitude: String = "",
+
+    @ColumnInfo(name = "latitude")
+    var latitude: String = "",
+
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "id")
+    val id: Int = 0
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bondoyap/ui/transactions/data/TransactionsDao.kt b/app/src/main/java/com/example/bondoyap/ui/transactions/data/TransactionsDao.kt
new file mode 100644
index 0000000000000000000000000000000000000000..d09b3619c20f83a05fc1a292f450f720efc04412
--- /dev/null
+++ b/app/src/main/java/com/example/bondoyap/ui/transactions/data/TransactionsDao.kt
@@ -0,0 +1,23 @@
+package com.example.bondoyap.ui.transactions.data
+
+import androidx.room.Dao
+import androidx.room.Delete
+import androidx.room.Query
+import androidx.room.Upsert
+import kotlinx.coroutines.flow.Flow
+
+@Dao
+interface TransactionsDao {
+
+    @Upsert
+    suspend fun upsertTransaction(transaksi: Transactions)
+
+    @Delete
+    suspend fun deleteTransaction(transaksi: Transactions)
+
+    @Query("SELECT * FROM transactions")
+    fun getTransactions(): Flow<List<Transactions>>
+
+    @Query("SELECT * FROM transactions WHERE transactions.id == :transactionsId")
+    suspend fun getTransactionById(transactionsId: Int?): Transactions
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bondoyap/ui/transactions/data/TransactionsRepository.kt b/app/src/main/java/com/example/bondoyap/ui/transactions/data/TransactionsRepository.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2a5dcda7972ac83f2c201507de6d43d2fe30c68d
--- /dev/null
+++ b/app/src/main/java/com/example/bondoyap/ui/transactions/data/TransactionsRepository.kt
@@ -0,0 +1,22 @@
+package com.example.bondoyap.ui.transactions.data
+
+import androidx.annotation.WorkerThread
+import kotlinx.coroutines.flow.Flow
+
+class TransactionsRepository(private val transactionsDao: TransactionsDao) {
+    val allTransactions: Flow<List<Transactions>> = transactionsDao.getTransactions()
+
+    @WorkerThread
+    suspend fun upsert(transactions: Transactions) {
+        transactionsDao.upsertTransaction(transactions)
+    }
+
+    @WorkerThread
+    suspend fun delete(transactions: Transactions) {
+        transactionsDao.deleteTransaction(transactions)
+    }
+
+    suspend fun get(transactionId: Int?): Transactions {
+        return transactionsDao.getTransactionById(transactionId)
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bondoyap/ui/transactions/data/TransactionsRoomDatabase.kt b/app/src/main/java/com/example/bondoyap/ui/transactions/data/TransactionsRoomDatabase.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ada79cee07b3f11afee9b2a627791b737ab9c644
--- /dev/null
+++ b/app/src/main/java/com/example/bondoyap/ui/transactions/data/TransactionsRoomDatabase.kt
@@ -0,0 +1,70 @@
+package com.example.bondoyap.ui.transactions.data
+
+import android.content.Context
+import androidx.room.Database
+import androidx.room.Room
+import androidx.room.RoomDatabase
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+@Database(
+    entities = [Transactions::class],
+    version = 3,
+    exportSchema = false
+)
+public abstract class TransactionsRoomDatabase: RoomDatabase() {
+
+    abstract fun transactionsDao(): TransactionsDao
+
+    companion object {
+        @Volatile
+        private var INSTANCE: TransactionsRoomDatabase? = null
+
+        fun getDatabase(context: Context): TransactionsRoomDatabase {
+            return INSTANCE ?: synchronized(this) {
+                val instance = Room.databaseBuilder(
+                    context.applicationContext,
+                    TransactionsRoomDatabase::class.java,
+                    "transactions_database"
+                ).addMigrations(MIGRATION_2_3).
+                build()
+                INSTANCE = instance
+                instance
+            }
+        }
+        private val MIGRATION_1_2: Migration = object : Migration(1, 2) {
+            override fun migrate(db: SupportSQLiteDatabase) {
+                db.execSQL("CREATE TABLE IF NOT EXISTS `transactions_new` " +
+                        "(`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " +
+                        "`judul` TEXT NOT NULL, " +
+                        "`nominal` REAL NOT NULL, " +
+                        "`is_pemasukan` INTEGER NOT NULL, " +
+                        "`tanggal` TEXT NOT NULL, " +
+                        "`lokasi` TEXT NOT NULL)")
+
+                db.execSQL("INSERT INTO transactions_new (id, judul, nominal, is_pemasukan) " +
+                        "SELECT id, judul, nominal, is_pemasukan FROM transactions")
+
+                db.execSQL("DROP TABLE transactions")
+
+                db.execSQL("ALTER TABLE transactions_new RENAME TO transactions")
+            }
+        }
+        private val MIGRATION_2_3: Migration = object : Migration(2, 3) {
+            override fun migrate(db: SupportSQLiteDatabase) {
+                db.execSQL("CREATE TABLE IF NOT EXISTS `transactions_new` " +
+                        "(`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " +
+                        "`judul` TEXT NOT NULL, " +
+                        "`nominal` REAL NOT NULL, " +
+                        "`is_pemasukan` INTEGER NOT NULL, " +
+                        "`tanggal` TEXT NOT NULL, " +
+                        "`longitude` TEXT NOT NULL, " +
+                        "`latitude` TEXT NOT NULL)")
+
+                db.execSQL("DROP TABLE transactions")
+
+                db.execSQL("ALTER TABLE transactions_new RENAME TO transactions")
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_pin.xml b/app/src/main/res/drawable/ic_pin.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4304772446917f94787245ca25f0020e4a904ccd
--- /dev/null
+++ b/app/src/main/res/drawable/ic_pin.xml
@@ -0,0 +1,10 @@
+<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,2C8.13,2 5,5.13 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.87 -3.13,-7 -7,-7zM12,11.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5s2.5,1.12 2.5,2.5s-1.12,2.5 -2.5,2.5z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_plus.xml b/app/src/main/res/drawable/ic_plus.xml
new file mode 100644
index 0000000000000000000000000000000000000000..eb0f40356c67383e0760d86a3ad14bcd11220a7d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_plus.xml
@@ -0,0 +1,11 @@
+<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="#000"
+        android:pathData="M19,13H13v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
+
+</vector>
\ 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 b12ccb0da844387368336a0eb6e22e5e60c954c5..bad97d63dc8bac143e499f7ff3a349305c72700f 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -3,8 +3,7 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/container"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    >
+    android:layout_height="match_parent">
 
     <com.google.android.material.bottomnavigation.BottomNavigationView
         android:id="@+id/nav_view"
diff --git a/app/src/main/res/layout/fragment_add_transactions.xml b/app/src/main/res/layout/fragment_add_transactions.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c7bc521ef2c2912a1614877a151b223a7d326f98
--- /dev/null
+++ b/app/src/main/res/layout/fragment_add_transactions.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:padding="16dp"
+    android:orientation="vertical">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/textfield_label_judul" />
+    <EditText
+        android:id="@+id/editText_judul"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:autofillHints="judul"
+        android:hint="@string/hint_judul"
+        android:inputType="text"
+        android:maxLength="150"
+        android:layout_marginBottom="16dp" />
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/textfield_label_nominal" />
+    <EditText
+        android:id="@+id/editText_nominal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:autofillHints="nominal"
+        android:hint="@string/hint_nominal"
+        android:inputType="numberDecimal"
+        android:maxLength="18"
+        android:layout_marginBottom="16dp" />
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/textfield_label_kategori" />
+    <Spinner
+        android:id="@+id/spinner_kategori"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="16dp" />
+
+    <Button
+        android:id="@+id/button_simpan"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/button_simpan" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_edit_transactions.xml b/app/src/main/res/layout/fragment_edit_transactions.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ea202d0be3584d842892ba196c00dd45967f3216
--- /dev/null
+++ b/app/src/main/res/layout/fragment_edit_transactions.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:padding="16dp"
+    android:orientation="vertical">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/textfield_label_judul" />
+    <EditText
+        android:id="@+id/editText_judul"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:autofillHints="judul"
+        android:hint="@string/hint_judul"
+        android:inputType="text"
+        android:maxLength="150"
+        android:layout_marginBottom="16dp" />
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/textfield_label_nominal" />
+    <EditText
+        android:id="@+id/editText_nominal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:autofillHints="nominal"
+        android:hint="@string/hint_nominal"
+        android:inputType="numberDecimal"
+        android:maxLength="18"
+        android:layout_marginBottom="16dp" />
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/textfield_label_kategori" />
+    <Spinner
+        android:id="@+id/spinner_kategori"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="16dp" />
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/textfield_label_lokasi" />
+    <EditText
+        android:id="@+id/editText_lokasi"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:inputType="text|textMultiLine"
+        android:gravity="top"/>
+
+    <CheckBox android:id="@+id/checkbox_update_lokasi"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Perbarui lokasi" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <Button
+            android:id="@+id/button_hapus"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:layout_marginEnd="8dp"
+            android:text="@string/button_hapus"
+            android:backgroundTint="@color/red"/>
+
+        <Button
+            android:id="@+id/button_update"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="@string/button_update" />
+
+    </LinearLayout>
+
+
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_transactions.xml b/app/src/main/res/layout/fragment_transactions.xml
index 45fe32c51b6329dc85f34563c32e86ecd2491f52..ad4fddd01c2a89be080305241b2b70399ef56d7a 100644
--- a/app/src/main/res/layout/fragment_transactions.xml
+++ b/app/src/main/res/layout/fragment_transactions.xml
@@ -1,22 +1,23 @@
-<?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"
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    tools:context=".ui.transactions.TransactionsFragment">
+    android:layout_marginTop="16dp">
 
-    <TextView
-        android:id="@+id/text_transactions"
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/recyclerViewTransactions"
         android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginBottom="60dp"/>
+
+    <com.google.android.material.floatingactionbutton.FloatingActionButton
+        android:id="@+id/button_addTransaction"
+        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" />
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
+        android:layout_alignParentEnd="true"
+        android:layout_alignParentBottom="true"
+        android:layout_marginEnd="24dp"
+        android:layout_marginBottom="72dp"
+        android:src="@drawable/ic_plus"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/recyclerview_transactions.xml b/app/src/main/res/layout/recyclerview_transactions.xml
new file mode 100644
index 0000000000000000000000000000000000000000..03524715210c6a87729033adc340fe07537e8e47
--- /dev/null
+++ b/app/src/main/res/layout/recyclerview_transactions.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/cardViewTransaction"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_margin="8dp"
+    app:cardCornerRadius="8dp"
+    app:cardElevation="4dp"
+    android:clickable="true"
+    android:focusable="true"
+    app:cardBackgroundColor="#af5eff">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:padding="16dp">
+
+        <LinearLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:orientation="vertical">
+
+            <TextView
+                android:id="@+id/transactionDate"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textSize="16sp"
+                android:text="Tanggal" />
+
+            <TextView
+                android:id="@+id/transactionTitle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textSize="20sp"
+                android:textStyle="bold"
+                android:layout_marginTop="5dp"
+                android:layout_marginBottom="5dp"
+                android:text="Judul" />
+
+            <TextView
+                android:id="@+id/transactionAmount"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textSize="16sp"
+                android:text="Nominal" />
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <TextView
+                android:id="@+id/transactionCategory"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textSize="16sp"
+                android:text="Kategori" />
+
+            <TextView
+                android:id="@+id/transactionLocation"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textSize="16sp"
+                android:text="Lokasi"
+                android:layout_marginTop="36dp"
+                app:drawableLeftCompat="@drawable/ic_pin" />
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+</androidx.cardview.widget.CardView>
diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml
index 4412b02a53bd550175ae936468dceed611158b3d..27c15a681a39c24c4ffedbc1e5ff8e7864537988 100644
--- a/app/src/main/res/navigation/mobile_navigation.xml
+++ b/app/src/main/res/navigation/mobile_navigation.xml
@@ -17,6 +17,18 @@
         android:label="@string/title_transactions"
         tools:layout="@layout/fragment_transactions" />
 
+    <fragment
+        android:id="@+id/navigation_add_transactions"
+        android:name="com.example.bondoyap.ui.transactions.AddTransactionsFragment"
+        android:label="@string/title_transactions"
+        tools:layout="@layout/fragment_add_transactions" />
+
+    <fragment
+        android:id="@+id/navigation_edit_transactions"
+        android:name="com.example.bondoyap.ui.transactions.EditTransactionsFragment"
+        android:label="@string/title_transactions"
+        tools:layout="@layout/fragment_edit_transactions" />
+
     <fragment
         android:id="@+id/navigation_scanner"
         android:name="com.example.bondoyap.ui.scanner.ScannerFragment"
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 09b8661f01111e613262986d67f2d021709e896c..d59eb366c2f88c41ccacee48df8917b49bf379b0 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -21,4 +21,18 @@
     <string name="random_transactions">Membuat transaksi random</string>
     <string name="save_transactions">Simpan daftar transaksi</string>
     <string name="send_transactions">Kirim daftar transaksi</string>
+
+    <string name="textfield_label_judul">Judul</string>
+    <string name="textfield_label_nominal">Nominal</string>
+    <string name="textfield_label_kategori">Kategori</string>
+    <string name="textfield_label_lokasi">Lokasi</string>
+    <string name="button_simpan">Simpan</string>
+    <string name="button_hapus">Hapus</string>
+    <string name="button_update">Update</string>
+    <string name="hint_judul">Enter Judul</string>
+    <string name="hint_nominal">Enter Nominal</string>
+    <string name="hint_kategori">Enter Kategori</string>
+    <string name="hint_lokasi">Enter Lokasi</string>
+    <string name="pemasukan">Pemasukan</string>
+    <string name="pengeluaran">Pengeluaran</string>
 </resources>
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index 31ed43cc99341f5b95f5b15fd54f84d14a585002..96e26d4e6b245ef5a2d5b0bba7d68c7d716a61d4 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -2,4 +2,5 @@
 plugins {
     id("com.android.application") version "8.3.0" apply false
     id("org.jetbrains.kotlin.android") version "1.9.22" apply false
+    id("com.google.devtools.ksp") version "1.9.22-1.0.17" apply false
 }
\ No newline at end of file