diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 0b0bdf7f7567038f51635a4452b86bd290d3113c..c20a2f04f868d614c9066e7a5f35c9a9c8e0c5a2 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -34,6 +34,12 @@ android {
     buildFeatures {
         viewBinding = true
     }
+
+    packaging {
+        resources {
+            excludes += "META-INF/*"
+        }
+    }
 }
 
 dependencies {
@@ -47,6 +53,7 @@ dependencies {
     implementation(libs.androidx.navigation.fragment.ktx)
     implementation(libs.androidx.navigation.ui.ktx)
     implementation(libs.androidx.lifecycle.viewmodel.compose)
+    implementation(libs.androidx.annotation)
     testImplementation(libs.junit)
     androidTestImplementation(libs.androidx.junit)
     androidTestImplementation(libs.androidx.espresso.core)
@@ -60,4 +67,16 @@ dependencies {
 
     // Chart
     implementation(libs.mpandroidchart)
+
+    // Retrofit
+    implementation(libs.retrofit2.retrofit)
+    implementation(libs.retrofit2.converter.gson)
+    implementation(libs.retrofit2.converter.scalars)
+    implementation(libs.okhttp3)
+
+    // Work
+    implementation(libs.androidx.work.runtime)
+
+    // Excel
+    implementation(libs.excelkt)
 }
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 497e69a30d2e3148e20ee25c48d931ee8caeb56e..4dd5ff7aea82efe33a729eefc3636fb98bb6e934 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,6 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools">
+    xmlns:tools="http://schemas.android.com/tools" >
+
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
 
     <application
         android:allowBackup="true"
@@ -10,11 +15,19 @@
         android:label="@string/app_name"
         android:roundIcon="@mipmap/ic_launcher_round"
         android:supportsRtl="true"
-        android:theme="@style/Theme.Bondoman"
-        >
+        android:theme="@style/Theme.Bondoman" >
+        <activity
+            android:name=".NoInternetActivity"
+            android:exported="false"
+            android:noHistory="true"
+            android:theme="@style/Theme.Bondoman" />
+        <activity
+            android:name=".ui.login.LoginActivity"
+            android:exported="false"
+            android:label="@string/title_activity_login"/>
         <activity
             android:name=".MainActivity"
-            android:exported="true">
+            android:exported="true" >
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
 
@@ -23,7 +36,21 @@
         </activity>
         <activity
             android:name=".AddTransactionActivity"
-            android:exported="false"/>
+            android:exported="false" />
+
+        <activity
+            android:name=".EditTransactionActivity"
+            android:exported="false" />
+
+        <provider
+            android:name="androidx.core.content.FileProvider"
+            android:authorities="${applicationId}.fileprovider"
+            android:exported="false"
+            android:grantUriPermissions="true" >
+            <meta-data
+                android:name="android.support.FILE_PROVIDER_PATHS"
+                android:resource="@xml/file_paths" />
+        </provider>
     </application>
 
 </manifest>
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/AddTransactionActivity.kt b/app/src/main/java/com/onionsquad/bondoman/AddTransactionActivity.kt
index 34088743ebf42d06a2a3dcd0815421bae4aedc49..e49b61b1892a12f7b0a11a5ec1e4386ab37c519f 100644
--- a/app/src/main/java/com/onionsquad/bondoman/AddTransactionActivity.kt
+++ b/app/src/main/java/com/onionsquad/bondoman/AddTransactionActivity.kt
@@ -2,11 +2,11 @@ package com.onionsquad.bondoman
 
 import android.content.Intent
 import android.os.Bundle
-import android.widget.Button
-import android.widget.EditText
 import androidx.appcompat.app.AppCompatActivity
 import androidx.lifecycle.ViewModelProvider
+import com.onionsquad.bondoman.databinding.ActivityAddTransactionBinding
 import com.onionsquad.bondoman.repository.TransactionRepository
+import com.onionsquad.bondoman.room.TransactionCategory
 import com.onionsquad.bondoman.room.TransactionDatabase
 import com.onionsquad.bondoman.room.TransactionEntity
 import com.onionsquad.bondoman.ui.transaction.TransactionViewModel
@@ -14,30 +14,29 @@ import com.onionsquad.bondoman.ui.transaction.TransactionViewModelFactory
 import java.util.Date
 
 class AddTransactionActivity : AppCompatActivity() {
-
+    private lateinit var binding: ActivityAddTransactionBinding
     private val database by lazy { TransactionDatabase.getInstance(this) }
     private val repository by lazy { TransactionRepository(database.transactionDao()) }
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        setContentView(R.layout.activity_add_transaction)
-
-        val titleEditText: EditText = findViewById(R.id.titleEditText)
-        val amountEditText: EditText = findViewById(R.id.amountEditText)
-        val categoryEditText: EditText = findViewById(R.id.categoryEditText)
-        val locationEditText: EditText = findViewById(R.id.locationEditText)
+        binding = ActivityAddTransactionBinding.inflate(layoutInflater)
+        setContentView(binding.root)
 
         val factory = TransactionViewModelFactory(repository)
         val viewModel = ViewModelProvider(this, factory)[TransactionViewModel::class.java]
 
-        val saveButton: Button = findViewById(R.id.saveButton)
-        saveButton.setOnClickListener {
-            val title = titleEditText.text.toString()
-            val amount = amountEditText.text.toString().toDoubleOrNull() ?: 0.0
-            val category = categoryEditText.text.toString()
-            val location = locationEditText.text.toString()
+        binding.saveButton.setOnClickListener {
+            val title = binding.titleEditText.text.toString()
+            val amount = binding.amountEditText.text.toString().toDoubleOrNull() ?: 0.0
+            val selectedCategoryId = binding.categoryRadioGroup.checkedRadioButtonId
+            val category = when (selectedCategoryId) {
+                R.id.incomeRadioButton -> TransactionCategory.INCOME
+                R.id.outcomeRadioButton -> TransactionCategory.OUTCOME
+                else -> TransactionCategory.OUTCOME
+            }
+            val location = binding.locationEditText.text.toString()
 
-            // Dapatkan tanggal saat ini
             val currentDate = Date()
 
             val transaction = TransactionEntity(
@@ -50,8 +49,6 @@ class AddTransactionActivity : AppCompatActivity() {
 
             viewModel.insertTransaction(transaction)
 
-
-            // Navigasi kembali ke halaman daftar transaksi
             Intent(this@AddTransactionActivity, MainActivity::class.java).also {
                 startActivity(it)
             }
diff --git a/app/src/main/java/com/onionsquad/bondoman/EditTransactionActivity.kt b/app/src/main/java/com/onionsquad/bondoman/EditTransactionActivity.kt
new file mode 100644
index 0000000000000000000000000000000000000000..5606ede65b8da631bc51b5024b1e7c5ff9ef8537
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/EditTransactionActivity.kt
@@ -0,0 +1,79 @@
+package com.onionsquad.bondoman
+
+import android.content.Intent
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.lifecycle.ViewModelProvider
+import com.onionsquad.bondoman.databinding.ActivityEditTransactionBinding
+import com.onionsquad.bondoman.repository.TransactionRepository
+import com.onionsquad.bondoman.room.TransactionCategory
+import com.onionsquad.bondoman.room.TransactionDatabase
+import com.onionsquad.bondoman.room.TransactionEntity
+import com.onionsquad.bondoman.ui.transaction.TransactionViewModel
+import com.onionsquad.bondoman.ui.transaction.TransactionViewModelFactory
+import java.util.Date
+
+class EditTransactionActivity : AppCompatActivity() {
+    private lateinit var binding: ActivityEditTransactionBinding
+    private val database by lazy { TransactionDatabase.getInstance(this) }
+    private val repository by lazy { TransactionRepository(database.transactionDao()) }
+    private lateinit var viewModel: TransactionViewModel
+
+    private var transactionId: Int = -1
+    private var transactionDate: Date = Date()
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        binding = ActivityEditTransactionBinding.inflate(layoutInflater)
+        setContentView(binding.root)
+
+        val factory = TransactionViewModelFactory(repository)
+        viewModel = ViewModelProvider(this, factory)[TransactionViewModel::class.java]
+
+        transactionId = intent.getIntExtra("transactionId", -1)
+        if (transactionId != -1) {
+            viewModel.getTransactionById(transactionId).observe(this) { transaction ->
+                if (transaction != null) {
+                    binding.titleEditText.setText(transaction.title)
+                    binding.amountEditText.setText(transaction.amount.toString())
+                    when (transaction.category) {
+                        TransactionCategory.INCOME -> binding.incomeRadioButton.isChecked = true
+                        TransactionCategory.OUTCOME -> binding.outcomeRadioButton.isChecked = true
+                        else -> {
+
+                        }
+                    }
+                    binding.locationEditText.setText(transaction.location)
+                    transactionDate = transaction.date
+                }
+            }
+        }
+
+        binding.saveButton.setOnClickListener {
+            val title = binding.titleEditText.text.toString()
+            val amount = binding.amountEditText.text.toString().toDoubleOrNull() ?: 0.0
+            val selectedCategoryId = binding.categoryRadioGroup.checkedRadioButtonId
+            val category = when (selectedCategoryId) {
+                R.id.incomeRadioButton -> TransactionCategory.INCOME
+                R.id.outcomeRadioButton -> TransactionCategory.OUTCOME
+                else -> TransactionCategory.OUTCOME
+            }
+            val location = binding.locationEditText.text.toString()
+
+            val updatedTransaction = TransactionEntity(
+                id = transactionId,
+                title = title,
+                amount = amount,
+                category = category,
+                date = transactionDate,
+                location = location
+            )
+
+            viewModel.updateTransaction(updatedTransaction)
+
+            Intent(this@EditTransactionActivity, MainActivity::class.java).also {
+                startActivity(it)
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/InternetActivity.kt b/app/src/main/java/com/onionsquad/bondoman/InternetActivity.kt
new file mode 100644
index 0000000000000000000000000000000000000000..34eb4e6274ac76be39f1ea50296ab38041e77fb1
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/InternetActivity.kt
@@ -0,0 +1,44 @@
+package com.onionsquad.bondoman
+
+import android.content.Context
+import android.net.ConnectivityManager
+import android.net.Network
+import android.net.NetworkCapabilities
+import android.net.NetworkRequest
+import android.os.Bundle
+import android.os.PersistableBundle
+import android.widget.Toast
+import androidx.appcompat.app.AppCompatActivity
+
+open class InternetActivity : AppCompatActivity() {
+
+    var isInternetConnected = false
+        private set
+
+    private val networkRequest = NetworkRequest.Builder()
+        .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+        .build()
+
+    private val networkCallback = object : ConnectivityManager.NetworkCallback() {
+        override fun onAvailable(network: Network) {
+            super.onAvailable(network)
+            isInternetConnected = true
+        }
+
+        override fun onLost(network: Network) {
+            super.onLost(network)
+            isInternetConnected = false
+            Toast.makeText(
+                this@InternetActivity,
+                R.string.internet_not_connected,
+                Toast.LENGTH_SHORT
+            ).show()
+        }
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+        connectivityManager.requestNetwork(networkRequest, networkCallback)
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/MainActivity.kt b/app/src/main/java/com/onionsquad/bondoman/MainActivity.kt
index 4212af32fbcabcdf8f4c3758bdf7d5ec4ca2df2e..e8285f954ad06c9e3d7c7745ded7efa6d477c647 100644
--- a/app/src/main/java/com/onionsquad/bondoman/MainActivity.kt
+++ b/app/src/main/java/com/onionsquad/bondoman/MainActivity.kt
@@ -1,5 +1,6 @@
 package com.onionsquad.bondoman
 
+import android.content.Intent
 import android.os.Bundle
 import androidx.appcompat.app.AppCompatActivity
 import androidx.navigation.fragment.findNavController
@@ -7,15 +8,23 @@ import androidx.navigation.ui.AppBarConfiguration
 import androidx.navigation.ui.setupActionBarWithNavController
 import androidx.navigation.ui.setupWithNavController
 import com.google.android.material.bottomnavigation.BottomNavigationView
+import com.onionsquad.bondoman.auth.AutoLogoutWorker
+import com.onionsquad.bondoman.auth.SessionManager
 import com.onionsquad.bondoman.databinding.ActivityMainBinding
+import com.onionsquad.bondoman.ui.login.LoginActivity
 
-class MainActivity : AppCompatActivity() {
+class MainActivity : InternetActivity() {
 
     private lateinit var binding: ActivityMainBinding
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
 
+        AutoLogoutWorker.start(this)
+
+        val sessionManager = SessionManager(this)
+        sessionManager.ensureAuthenticated()
+
         binding = ActivityMainBinding.inflate(layoutInflater)
         setContentView(binding.root)
 
diff --git a/app/src/main/java/com/onionsquad/bondoman/NoInternetActivity.kt b/app/src/main/java/com/onionsquad/bondoman/NoInternetActivity.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c17e7959a6d92b4be05ee8c321ff3e198b0550ff
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/NoInternetActivity.kt
@@ -0,0 +1,17 @@
+package com.onionsquad.bondoman
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import com.onionsquad.bondoman.databinding.ActivityNoInternetBinding
+
+class NoInternetActivity : AppCompatActivity() {
+
+    private lateinit var binding: ActivityNoInternetBinding
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        binding = ActivityNoInternetBinding.inflate(layoutInflater)
+        setContentView(binding.root)
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/auth/AutoLogoutWorker.kt b/app/src/main/java/com/onionsquad/bondoman/auth/AutoLogoutWorker.kt
new file mode 100644
index 0000000000000000000000000000000000000000..bbf7a225b6c8024633518557dbfff5b6b2eaa713
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/auth/AutoLogoutWorker.kt
@@ -0,0 +1,58 @@
+package com.onionsquad.bondoman.auth
+
+import android.content.Context
+import android.util.Log
+import androidx.work.ExistingWorkPolicy
+import androidx.work.OneTimeWorkRequestBuilder
+import androidx.work.WorkManager
+import androidx.work.Worker
+import androidx.work.WorkerParameters
+import java.time.Instant
+import java.util.concurrent.TimeUnit
+import kotlin.math.max
+
+
+class AutoLogoutWorker(
+    private val context: Context,
+    private val workerParameters: WorkerParameters
+) : Worker(context, workerParameters) {
+
+    override fun doWork(): Result {
+        Log.d(this::class.java.simpleName, "Proceed auto logout")
+        val sessionManager = SessionManager(context)
+        sessionManager.deleteAuthToken()
+        return Result.success()
+    }
+
+    companion object {
+        fun start(context: Context) {
+            Log.d(AutoLogoutWorker::class.java.simpleName, "Starting auto logout worker")
+            val sessionManager = SessionManager(context)
+            val token = sessionManager.fetchAuthToken()
+            if (token != null) {
+                val duration = max(0, token.exp - Instant.now().epochSecond)
+                Log.d(AutoLogoutWorker::class.java.simpleName, "Proceed to logout in $duration secs")
+                val workRequest = OneTimeWorkRequestBuilder<AutoLogoutWorker>()
+                    .setInitialDelay(duration, TimeUnit.SECONDS)
+                    .build()
+                WorkManager
+                    .getInstance(context)
+                    .enqueueUniqueWork(
+                        AutoLogoutWorker::class.java.simpleName,
+                        ExistingWorkPolicy.REPLACE,
+                        workRequest
+                    )
+            } else {
+                stop(context)
+            }
+        }
+
+        fun stop(context: Context) {
+            Log.d(AutoLogoutWorker::class.java.simpleName, "Stopping auto logout worker")
+            WorkManager
+                .getInstance(context)
+                .cancelUniqueWork(AutoLogoutWorker::class.java.simpleName)
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/auth/SessionManager.kt b/app/src/main/java/com/onionsquad/bondoman/auth/SessionManager.kt
new file mode 100644
index 0000000000000000000000000000000000000000..1089d87fad8930e1e229c7f55109bbc33d601161
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/auth/SessionManager.kt
@@ -0,0 +1,41 @@
+package com.onionsquad.bondoman.auth
+
+import android.content.Context
+import android.content.Intent
+import android.util.Log
+import com.google.gson.Gson
+import com.onionsquad.bondoman.R
+import com.onionsquad.bondoman.ui.login.LoginActivity
+
+class SessionManager(private val context: Context) {
+    private val sharedPreferences = context.getSharedPreferences(
+        context.getString(R.string.preference_file_key),
+        Context.MODE_PRIVATE
+    )
+
+    companion object {
+        const val USER_TOKEN = "user_token"
+    }
+
+    fun fetchAuthToken(): Token? {
+        val tokenJson = sharedPreferences.getString(USER_TOKEN, null)
+        return if (tokenJson != null) Gson().fromJson(tokenJson, Token::class.java) else null
+    }
+
+    fun saveAuthToken(token: Token) {
+        sharedPreferences.edit().putString(USER_TOKEN, Gson().toJson(token)).apply()
+        Log.d("TOKEN", "${fetchAuthToken()}")
+    }
+
+    fun deleteAuthToken() {
+        sharedPreferences.edit().remove(USER_TOKEN).apply()
+    }
+
+    fun ensureAuthenticated() {
+        if (fetchAuthToken() == null) {
+            val intent = Intent(context, LoginActivity::class.java)
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+            context.startActivity(intent)
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/auth/Token.kt b/app/src/main/java/com/onionsquad/bondoman/auth/Token.kt
new file mode 100644
index 0000000000000000000000000000000000000000..435b5dba0a1767c56fbcaa233912f555fb34b0c8
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/auth/Token.kt
@@ -0,0 +1,3 @@
+package com.onionsquad.bondoman.auth
+
+data class Token(val tokenString: String, val nim: String, val exp: Long, val iat: Long)
diff --git a/app/src/main/java/com/onionsquad/bondoman/network/BondomanApiService.kt b/app/src/main/java/com/onionsquad/bondoman/network/BondomanApiService.kt
new file mode 100644
index 0000000000000000000000000000000000000000..5ecec9762bcbbb22be1f663763a71a9c8b18343b
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/network/BondomanApiService.kt
@@ -0,0 +1,36 @@
+package com.onionsquad.bondoman.network
+
+import okhttp3.OkHttpClient
+import retrofit2.Call
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+import retrofit2.converter.scalars.ScalarsConverterFactory
+import retrofit2.http.Body
+import retrofit2.http.Header
+import retrofit2.http.Headers
+import retrofit2.http.POST
+
+
+private const val BASE_URL = "https://pbd-backend-2024.vercel.app/"
+private val client = OkHttpClient.Builder().build()
+private val retrofit = Retrofit.Builder()
+    .baseUrl(BASE_URL)
+    .addConverterFactory(ScalarsConverterFactory.create())
+    .addConverterFactory(GsonConverterFactory.create())
+    .client(client)
+    .build()
+
+interface BondomanApiService {
+    @Headers("Content-Type: application/json")
+    @POST("api/auth/login")
+    fun login(@Body loginData: LoginRequest): Call<String>
+
+    @POST("api/auth/token")
+    fun token(@Header("Authorization") token: String): Call<TokenResponse>
+}
+
+object BondomanApi {
+    val retrofitService: BondomanApiService by lazy {
+        retrofit.create(BondomanApiService::class.java)
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/network/LoginRequest.kt b/app/src/main/java/com/onionsquad/bondoman/network/LoginRequest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c2a6d36c6bc1bf96f6c3af2aee461c9c78946eec
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/network/LoginRequest.kt
@@ -0,0 +1,3 @@
+package com.onionsquad.bondoman.network
+
+data class LoginRequest(val email: String?, val password: String?)
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/network/LoginResponse.kt b/app/src/main/java/com/onionsquad/bondoman/network/LoginResponse.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c264da3200c401f27b1c04755eb4aa154d44bbb1
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/network/LoginResponse.kt
@@ -0,0 +1,5 @@
+package com.onionsquad.bondoman.network
+
+import com.google.gson.annotations.SerializedName
+
+data class LoginResponse(val token: String?)
diff --git a/app/src/main/java/com/onionsquad/bondoman/network/TokenResponse.kt b/app/src/main/java/com/onionsquad/bondoman/network/TokenResponse.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ed2cc004cb0bcc9941a021053d1a92fb622eab8b
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/network/TokenResponse.kt
@@ -0,0 +1,3 @@
+package com.onionsquad.bondoman.network
+
+data class TokenResponse(val nim: String?, val exp: Long?, val iat: Long?)
diff --git a/app/src/main/java/com/onionsquad/bondoman/repository/TransactionRepository.kt b/app/src/main/java/com/onionsquad/bondoman/repository/TransactionRepository.kt
index ac46a924739ec3fe9c2e64c6773d2025fdd85d17..584f0e36d7b8a05e698c858cc62e440d02539ef0 100644
--- a/app/src/main/java/com/onionsquad/bondoman/repository/TransactionRepository.kt
+++ b/app/src/main/java/com/onionsquad/bondoman/repository/TransactionRepository.kt
@@ -19,4 +19,8 @@ class TransactionRepository(private val transactionDao: TransactionDao) {
     suspend fun deleteTransaction(transaction: TransactionEntity) {
         transactionDao.deleteTransaction(transaction)
     }
+
+    fun getTransactionById(id: Int): LiveData<TransactionEntity> {
+        return transactionDao.getTransactionById(id)
+    }
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/room/TransactionCategory.kt b/app/src/main/java/com/onionsquad/bondoman/room/TransactionCategory.kt
new file mode 100644
index 0000000000000000000000000000000000000000..f37764b16572f2d510b4d0c2a12c54abae909df7
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/room/TransactionCategory.kt
@@ -0,0 +1,7 @@
+package com.onionsquad.bondoman.room
+
+enum class TransactionCategory {
+    INCOME,
+    OUTCOME,
+    UNKNOWN
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/room/TransactionDao.kt b/app/src/main/java/com/onionsquad/bondoman/room/TransactionDao.kt
index 31e75bd6384c1a356ccaa4c5b4939af37434a8f9..601fb1c31a914a5f7f355f08dea8b2751c5995eb 100644
--- a/app/src/main/java/com/onionsquad/bondoman/room/TransactionDao.kt
+++ b/app/src/main/java/com/onionsquad/bondoman/room/TransactionDao.kt
@@ -22,4 +22,7 @@ interface TransactionDao {
 
     @Query("SELECT * FROM transactions WHERE category = 'OUTCOME'")
     fun getAllOutcomes(): LiveData<List<TransactionEntity>>
+
+    @Query("SELECT * FROM transactions WHERE id = :id")
+    fun getTransactionById(id: Int): LiveData<TransactionEntity>
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/room/TransactionEntity.kt b/app/src/main/java/com/onionsquad/bondoman/room/TransactionEntity.kt
index 66c176602c7f51214ba4ddd47de96c2ae911e837..64898cbf7c8ebf97163227d9ddd15a23450d0bc8 100644
--- a/app/src/main/java/com/onionsquad/bondoman/room/TransactionEntity.kt
+++ b/app/src/main/java/com/onionsquad/bondoman/room/TransactionEntity.kt
@@ -4,11 +4,11 @@ import androidx.room.Entity
 import androidx.room.PrimaryKey
 import androidx.room.ColumnInfo
 import androidx.room.TypeConverters
-import com.onionsquad.bondoman.util.DateConverter
+import com.onionsquad.bondoman.util.Converters
 import java.util.Date
 
 @Entity(tableName = "transactions")
-@TypeConverters(DateConverter::class)
+@TypeConverters(Converters::class)
 data class TransactionEntity(
     @PrimaryKey(autoGenerate = true)
     val id: Int = 0,
@@ -20,7 +20,7 @@ data class TransactionEntity(
     val amount: Double,
 
     @ColumnInfo(name = "category")
-    val category: String,
+    val category: TransactionCategory,
 
     @ColumnInfo(name = "date", defaultValue = "CURRENT_TIMESTAMP")
     val date: Date,
diff --git a/app/src/main/java/com/onionsquad/bondoman/ui/login/LoginActivity.kt b/app/src/main/java/com/onionsquad/bondoman/ui/login/LoginActivity.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2774d6bf67bd5c8c4385e964a7d047f1fe3d4a7d
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/ui/login/LoginActivity.kt
@@ -0,0 +1,149 @@
+package com.onionsquad.bondoman.ui.login
+
+import android.app.Activity
+import android.content.Intent
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProvider
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import android.text.Editable
+import android.text.TextWatcher
+import android.view.View
+import android.view.inputmethod.EditorInfo
+import android.widget.EditText
+import android.widget.Toast
+import androidx.work.ExistingWorkPolicy
+import androidx.work.OneTimeWorkRequestBuilder
+import androidx.work.PeriodicWorkRequestBuilder
+import androidx.work.WorkManager
+import com.onionsquad.bondoman.InternetActivity
+import com.onionsquad.bondoman.MainActivity
+import com.onionsquad.bondoman.NoInternetActivity
+import com.onionsquad.bondoman.databinding.ActivityLoginBinding
+
+import com.onionsquad.bondoman.R
+import com.onionsquad.bondoman.auth.AutoLogoutWorker
+import com.onionsquad.bondoman.auth.SessionManager
+import java.time.Instant
+import java.util.concurrent.TimeUnit
+
+class LoginActivity : InternetActivity() {
+
+    private lateinit var loginViewModel: LoginViewModel
+    private lateinit var binding: ActivityLoginBinding
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        binding = ActivityLoginBinding.inflate(layoutInflater)
+        setContentView(binding.root)
+
+        val email = binding.username
+        val password = binding.password
+        val login = binding.login
+        val loading = binding.loading
+
+        val sessionManager = SessionManager(this)
+
+        loginViewModel = ViewModelProvider(this)[LoginViewModel::class.java]
+            .apply {
+                loginFormState.observe(this@LoginActivity, Observer {
+                    val loginState = it ?: return@Observer
+
+                    login.isEnabled = loginState.isDataValid
+
+                    if (loginState.emailError != null) {
+                        email.error = getString(loginState.emailError)
+                    }
+                })
+                loginResult.observe(this@LoginActivity, Observer {
+                    val loginResult = it ?: return@Observer
+
+                    loading.visibility = View.GONE
+                    if (loginResult.error != null) {
+                        showLoginFailed(loginResult.error)
+                    }
+                    if (loginResult.success != null) {
+                        sessionManager.saveAuthToken(loginResult.success)
+                        AutoLogoutWorker.start(this@LoginActivity)
+                        showLoginSuccess()
+                        sendToMainActivity()
+                    }
+                })
+            }
+
+        email.afterTextChanged {
+            loginViewModel.loginDataChanged(
+                email.text.toString(),
+                password.text.toString()
+            )
+        }
+
+        password.apply {
+            afterTextChanged {
+                loginViewModel.loginDataChanged(
+                    email.text.toString(),
+                    password.text.toString()
+                )
+            }
+
+            setOnEditorActionListener { _, actionId, _ ->
+                when (actionId) {
+                    EditorInfo.IME_ACTION_DONE -> {
+                        if (!isInternetConnected) {
+                            sendToNoInternetActivity()
+                        } else {
+                            loginViewModel.login(
+                                email.text.toString(),
+                                password.text.toString()
+                            )
+                        }
+                    }
+                }
+                false
+            }
+
+            login.setOnClickListener {
+                if (!isInternetConnected) {
+                    sendToNoInternetActivity()
+                    return@setOnClickListener
+                } else {
+                    loading.visibility = View.VISIBLE
+                    loginViewModel.login(email.text.toString(), password.text.toString())
+                }
+            }
+        }
+    }
+
+    private fun sendToNoInternetActivity() {
+        val intent = Intent(this@LoginActivity, NoInternetActivity::class.java)
+        startActivity(intent)
+    }
+
+    private fun sendToMainActivity() {
+        val intent = Intent(this@LoginActivity, MainActivity::class.java)
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+        startActivity(intent)
+        finish()
+    }
+
+    private fun showLoginSuccess() {
+        Toast.makeText(applicationContext, R.string.login_success, Toast.LENGTH_LONG).show()
+    }
+
+    private fun showLoginFailed(error: String) {
+        Toast.makeText(applicationContext, error, Toast.LENGTH_SHORT).show()
+    }
+}
+
+fun EditText.afterTextChanged(afterTextChanged: (String) -> Unit) {
+    this.addTextChangedListener(object : TextWatcher {
+        override fun afterTextChanged(editable: Editable?) {
+            afterTextChanged.invoke(editable.toString())
+        }
+
+        override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
+
+        override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
+    })
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/ui/login/LoginFormState.kt b/app/src/main/java/com/onionsquad/bondoman/ui/login/LoginFormState.kt
new file mode 100644
index 0000000000000000000000000000000000000000..dc8eb07260f3c148cec77ef1264538bf0588ed97
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/ui/login/LoginFormState.kt
@@ -0,0 +1,6 @@
+package com.onionsquad.bondoman.ui.login
+
+data class LoginFormState(
+    val emailError: Int? = null,
+    val isDataValid: Boolean = false
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/ui/login/LoginResult.kt b/app/src/main/java/com/onionsquad/bondoman/ui/login/LoginResult.kt
new file mode 100644
index 0000000000000000000000000000000000000000..25ce877ebd7c45ae4ca479ce9be4a4aad7c71ed5
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/ui/login/LoginResult.kt
@@ -0,0 +1,8 @@
+package com.onionsquad.bondoman.ui.login
+
+import com.onionsquad.bondoman.auth.Token
+
+data class LoginResult(
+    val success: Token? = null,
+    val error: String? = null
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/ui/login/LoginViewModel.kt b/app/src/main/java/com/onionsquad/bondoman/ui/login/LoginViewModel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..6cb095fbcddae41cb7ce9b7f78e243d2ea8766d3
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/ui/login/LoginViewModel.kt
@@ -0,0 +1,87 @@
+package com.onionsquad.bondoman.ui.login
+
+import android.util.Log
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import android.util.Patterns
+import com.google.gson.Gson
+
+import com.onionsquad.bondoman.R
+import com.onionsquad.bondoman.auth.SessionManager
+import com.onionsquad.bondoman.auth.Token
+import com.onionsquad.bondoman.network.BondomanApi
+import com.onionsquad.bondoman.network.LoginRequest
+import com.onionsquad.bondoman.network.LoginResponse
+import com.onionsquad.bondoman.network.TokenResponse
+import retrofit2.Call
+import retrofit2.Callback
+import retrofit2.Response
+
+class LoginViewModel : ViewModel() {
+
+    private val _loginForm = MutableLiveData<LoginFormState>()
+    val loginFormState: LiveData<LoginFormState> = _loginForm
+
+    private val _loginResult = MutableLiveData<LoginResult>()
+    val loginResult: LiveData<LoginResult> = _loginResult
+
+    fun login(email: String, password: String) {
+        BondomanApi.retrofitService.login(LoginRequest(email, password)).enqueue(
+            object : Callback<String> {
+                override fun onResponse(call: Call<String>, response: Response<String>) {
+                    if (response.isSuccessful) {
+                        val body = response.body()
+                        val tokenString = Gson().fromJson(body, LoginResponse::class.java).token!!
+                        Log.d("TOKEN", tokenString)
+                        fetchTokenInformation(tokenString)
+                    } else {
+                        val errBody = response.errorBody()!!.string()
+                        val error = when {
+                            errBody.contains("email") -> "Invalid email"
+                            errBody.contains("password") -> "Invalid password"
+                            else -> "Invalid email or password"
+                        }
+                        _loginResult.value = LoginResult(error = error)
+                    }
+                }
+
+                override fun onFailure(call: Call<String>, t: Throwable) {
+                    _loginResult.value = LoginResult(error = t.message)
+                }
+            }
+        )
+    }
+
+    private fun fetchTokenInformation(tokenString: String) {
+        BondomanApi.retrofitService.token("Bearer $tokenString").enqueue(
+            object : Callback<TokenResponse> {
+                override fun onResponse(
+                    call: Call<TokenResponse>,
+                    response: Response<TokenResponse>
+                ) {
+                    val token = response.body()!!
+                    _loginResult.value = LoginResult(
+                        success = Token(tokenString, token.nim!!, token.exp!!, token.iat!!)
+                    )
+                }
+
+                override fun onFailure(call: Call<TokenResponse>, t: Throwable) {
+                    _loginResult.value = LoginResult(error = t.message)
+                }
+            }
+        )
+    }
+
+    fun loginDataChanged(email: String, password: String) {
+        if (!isEmailValid(email)) {
+            _loginForm.value = LoginFormState(emailError = R.string.invalid_email)
+        } else {
+            _loginForm.value = LoginFormState(isDataValid = true)
+        }
+    }
+
+    private fun isEmailValid(username: String): Boolean {
+        return Patterns.EMAIL_ADDRESS.matcher(username).matches()
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/ui/settings/SettingsFragment.kt b/app/src/main/java/com/onionsquad/bondoman/ui/settings/SettingsFragment.kt
index dc153a26a1d0a02e397c9400600979bbc3a8be75..1219bbe14f7a5a8503001852174bbf81f0539ad8 100644
--- a/app/src/main/java/com/onionsquad/bondoman/ui/settings/SettingsFragment.kt
+++ b/app/src/main/java/com/onionsquad/bondoman/ui/settings/SettingsFragment.kt
@@ -1,35 +1,220 @@
 package com.onionsquad.bondoman.ui.settings
 
+import android.app.AlertDialog
+import android.content.Intent
 import android.os.Bundle
+import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import android.widget.ArrayAdapter
+import android.widget.Toast
+import androidx.activity.result.contract.ActivityResultContracts.CreateDocument
+import androidx.core.content.FileProvider
 import androidx.fragment.app.Fragment
-import androidx.lifecycle.ViewModelProvider
+import androidx.navigation.fragment.findNavController
+import com.onionsquad.bondoman.InternetActivity
+import com.onionsquad.bondoman.NoInternetActivity
+import com.onionsquad.bondoman.R
+import com.onionsquad.bondoman.auth.AutoLogoutWorker
+import com.onionsquad.bondoman.auth.SessionManager
 import com.onionsquad.bondoman.databinding.FragmentSettingsBinding
+import com.onionsquad.bondoman.repository.TransactionRepository
+import com.onionsquad.bondoman.room.TransactionCategory
+import com.onionsquad.bondoman.room.TransactionDatabase
+import com.onionsquad.bondoman.room.TransactionEntity
+import io.github.evanrupert.excelkt.workbook
+import org.apache.poi.ss.usermodel.FillPatternType
+import org.apache.poi.ss.usermodel.IndexedColors
+import java.io.File
+import java.io.OutputStream
+import java.time.ZoneId
+import java.time.format.DateTimeFormatter
 
 class SettingsFragment : Fragment() {
     private var _binding: FragmentSettingsBinding? = null
 
-    // This property is only valid between onCreateView and
-    // onDestroyView.
     private val binding get() = _binding!!
 
+    private val database by lazy { TransactionDatabase.getInstance(requireContext()) }
+    private val repository by lazy { TransactionRepository(database.transactionDao()) }
+
+
+    private val saveXls =
+        registerForActivityResult(CreateDocument("application/vnd.ms-excel")) { uri ->
+            if (uri != null) {
+                repository.listTransactions.observe(viewLifecycleOwner) { list ->
+                    Log.d(this::class.java.simpleName, "Saving transactions to $uri")
+                    requireContext().contentResolver.openOutputStream(uri)?.use { fos ->
+                        createExcelFile(list, fos)
+                    }
+                }
+            }
+        }
+
+    private val saveXlsx =
+        registerForActivityResult(
+            CreateDocument("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
+        ) { uri ->
+            if (uri != null) {
+                repository.listTransactions.observe(viewLifecycleOwner) { list ->
+                    Log.d(this::class.java.simpleName, "Saving transactions to $uri")
+                    requireContext().contentResolver.openOutputStream(uri)?.use { fos ->
+                        createExcelFile(list, fos)
+                    }
+                }
+            }
+        }
+
     override fun onCreateView(
         inflater: LayoutInflater,
         container: ViewGroup?,
         savedInstanceState: Bundle?
     ): View {
-        val transactionViewModel =
-            ViewModelProvider(this).get(SettingsViewModel::class.java)
-
         _binding = FragmentSettingsBinding.inflate(inflater, container, false)
 
+        val sessionManager = SessionManager(requireContext())
+
+        binding.apply {
+            buttonLogout.setOnClickListener {
+                val alertBuilder = AlertDialog.Builder(requireContext())
+                alertBuilder.setTitle(R.string.title_alert_logout)
+                alertBuilder.setMessage(R.string.message_alert_logout)
+                alertBuilder.setPositiveButton(R.string.yes) { _, _ ->
+                    AutoLogoutWorker.stop(requireContext())
+                    logout()
+                }
+                alertBuilder.setNegativeButton(R.string.no) { dialog, _ ->
+                    dialog.cancel()
+                }
+                alertBuilder.show()
+            }
+
+            ArrayAdapter.createFromResource(
+                requireContext(),
+                R.array.excel_types,
+                android.R.layout.simple_spinner_dropdown_item
+            ).also { arrayAdapter ->
+                arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
+                spinnerExcelType.adapter = arrayAdapter
+            }
+
+            buttonSave.setOnClickListener {
+                when (spinnerExcelType.selectedItem.toString()) {
+                    "XLS" -> saveXls.launch("transactions.xls")
+                    "XLSX" -> saveXlsx.launch("transactions.xlsx")
+                }
+                Toast.makeText(requireContext(), R.string.transactions_saved, Toast.LENGTH_SHORT)
+                    .show()
+            }
+
+            buttonSendEmail.setOnClickListener {
+                if (!(requireActivity() as InternetActivity).isInternetConnected) {
+                    sendToNoInternetActivity()
+                    return@setOnClickListener
+                }
+                sessionManager.ensureAuthenticated()
+                val token = sessionManager.fetchAuthToken()!!
+                val intent = Intent(Intent.ACTION_SEND).apply {
+                    type = "text/html"
+                    putExtra(Intent.EXTRA_EMAIL, arrayOf("${token.nim}@std.stei.itb.ac.id"))
+                    putExtra(Intent.EXTRA_SUBJECT, "Daftar Transaksi Aplikasi Bondoman")
+                    putExtra(
+                        Intent.EXTRA_TEXT,
+                        "Halo ${token.nim}, berikut daftar transaksi yang telah kamu catat dalam aplikasi Bondoman"
+                    )
+                    addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+                    val tempFile = File(requireContext().cacheDir, "transactions.xlsx")
+                    if (tempFile.exists()) {
+                        tempFile.delete()
+                    } else {
+                        tempFile.parentFile?.mkdirs()
+                    }
+                    val fileUri = FileProvider.getUriForFile(
+                        requireContext(),
+                        "${requireContext().packageName}.fileprovider",
+                        tempFile
+                    )
+                    repository.listTransactions.observe(viewLifecycleOwner) { list ->
+                        requireContext().contentResolver.openOutputStream(fileUri)?.use { fos ->
+                            createExcelFile(list, fos)
+                        }
+                    }
+                    putExtra(Intent.EXTRA_STREAM, fileUri)
+                }
+                startActivity(Intent.createChooser(intent, "Send email"))
+                Toast.makeText(requireContext(), R.string.transactions_sent, Toast.LENGTH_SHORT)
+                    .show()
+            }
+        }
+
         return binding.root
     }
 
+    private fun sendToNoInternetActivity() {
+        val intent = Intent(requireContext(), NoInternetActivity::class.java)
+        startActivity(intent)
+    }
+
     override fun onDestroyView() {
         super.onDestroyView()
         _binding = null
     }
+
+    private fun logout() {
+        val sessionManager = SessionManager(requireContext())
+        sessionManager.deleteAuthToken()
+        Toast.makeText(requireContext(), R.string.log_out_success, Toast.LENGTH_SHORT).show()
+        findNavController().popBackStack(R.id.navigation_transaction, true)
+        requireActivity().recreate()
+    }
+
+    private fun createExcelFile(data: List<TransactionEntity>, outputStream: OutputStream) {
+        workbook {
+            val headers = arrayOf(
+                "Tanggal",
+                "Kategori Transaksi",
+                "Nominal Transaksi",
+                "Nama Transaksi",
+                "Lokasi"
+            )
+            val sheet = sheet {
+                val headingStyle = createCellStyle {
+                    val font = createFont { bold = true }
+                    setFont(font)
+                    fillPattern = FillPatternType.SOLID_FOREGROUND
+                    fillForegroundColor = IndexedColors.AQUA.index
+                }
+                row(headingStyle) {
+                    headers.forEach { cell(it) }
+                }
+                for (transaction in data) {
+                    row {
+                        val date = transaction.date
+                            .toInstant()
+                            .atZone(ZoneId.systemDefault())
+                            .toLocalDateTime()
+                            .format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss"))
+                        cell(date)
+                        cell(transaction.category.text())
+                        cell(transaction.amount)
+                        cell(transaction.title)
+                        cell(transaction.location)
+                    }
+                }
+            }.xssfSheet
+            for (i in 0..headers.size) {
+                sheet.setColumnWidth(i, 30 * 256)
+            }
+        }.xssfWorkbook.write(outputStream)
+    }
+
+    private fun TransactionCategory.text(): String {
+        return when (this) {
+            TransactionCategory.INCOME -> "Pemasukan"
+            TransactionCategory.OUTCOME -> "Pengeluaran"
+            TransactionCategory.UNKNOWN -> ""
+        }
+
+    }
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/ui/settings/SettingsViewModel.kt b/app/src/main/java/com/onionsquad/bondoman/ui/settings/SettingsViewModel.kt
deleted file mode 100644
index a877767c5378529d02be28a4688b0589b13d6064..0000000000000000000000000000000000000000
--- a/app/src/main/java/com/onionsquad/bondoman/ui/settings/SettingsViewModel.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.onionsquad.bondoman.ui.settings
-
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.ViewModel
-
-class SettingsViewModel : 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/com/onionsquad/bondoman/ui/transaction/TransactionAdapter.kt b/app/src/main/java/com/onionsquad/bondoman/ui/transaction/TransactionAdapter.kt
new file mode 100644
index 0000000000000000000000000000000000000000..b39a926ac4c347ae5a3e08f8d1763d99622c9af9
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/ui/transaction/TransactionAdapter.kt
@@ -0,0 +1,53 @@
+package com.onionsquad.bondoman.ui.transaction
+
+import android.content.Intent
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.onionsquad.bondoman.EditTransactionActivity
+import com.onionsquad.bondoman.databinding.TransactionCardBinding
+import com.onionsquad.bondoman.room.TransactionEntity
+
+class TransactionAdapter(
+    private val viewModel: TransactionViewModel,
+    private var transactionList: List<TransactionEntity> = emptyList()
+) : RecyclerView.Adapter<TransactionAdapter.ViewHolder>() {
+
+    inner class ViewHolder(private val binding: TransactionCardBinding) : RecyclerView.ViewHolder(binding.root) {
+        fun bind(transaction: TransactionEntity) {
+            binding.titleTextView.text = transaction.title
+            binding.amountTextView.text = "IDR ".plus(transaction.amount)
+            binding.categoryTextView.text = transaction.category.toString()
+            binding.dateTextView.text = transaction.date.toString()
+            binding.locationTextView.text = transaction.location
+
+            binding.deleteButton.setOnClickListener {
+                viewModel.deleteTransaction(transaction)
+            }
+            binding.editButton.setOnClickListener {
+                val intent = Intent(binding.root.context, EditTransactionActivity::class.java)
+                intent.putExtra("transactionId", transaction.id)
+                binding.root.context.startActivity(intent)
+            }
+        }
+    }
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+        val binding = TransactionCardBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+        return ViewHolder(binding)
+    }
+
+    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+        val transaction = transactionList[position]
+        holder.bind(transaction)
+    }
+
+    override fun getItemCount(): Int {
+        return transactionList.size
+    }
+
+    fun setTransactionList(transactionList: List<TransactionEntity>) {
+        this.transactionList = transactionList
+        notifyDataSetChanged()
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/ui/transaction/TransactionFragment.kt b/app/src/main/java/com/onionsquad/bondoman/ui/transaction/TransactionFragment.kt
index 4ff1ccc32f50abe95a0e0e74e85e2595bf8997e6..7af3c31b9b13576aaee461b52885ff04ae8710f7 100644
--- a/app/src/main/java/com/onionsquad/bondoman/ui/transaction/TransactionFragment.kt
+++ b/app/src/main/java/com/onionsquad/bondoman/ui/transaction/TransactionFragment.kt
@@ -7,38 +7,60 @@ import android.view.View
 import android.view.ViewGroup
 import androidx.fragment.app.Fragment
 import androidx.lifecycle.ViewModelProvider
+import androidx.recyclerview.widget.LinearLayoutManager
 import com.onionsquad.bondoman.AddTransactionActivity
 import com.onionsquad.bondoman.databinding.FragmentTransactionBinding
 import com.onionsquad.bondoman.repository.TransactionRepository
 import com.onionsquad.bondoman.room.TransactionDatabase
 
 class TransactionFragment : Fragment() {
-
     private var _binding: FragmentTransactionBinding? = null
-
-    // This property is only valid between onCreateView and
-    // onDestroyView.
     private val binding get() = _binding!!
 
     private val database by lazy { TransactionDatabase.getInstance(requireContext().applicationContext) }
     private val repository by lazy { TransactionRepository(database.transactionDao()) }
+    private lateinit var transactionViewModel: TransactionViewModel
+    private lateinit var transactionAdapter: TransactionAdapter
 
     override fun onCreateView(
-            inflater: LayoutInflater,
-            container: ViewGroup?,
-            savedInstanceState: Bundle?
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
     ): View {
+        _binding = FragmentTransactionBinding.inflate(inflater, container, false)
+        return binding.root
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        setupViewModel()
+        setupRecyclerView()
+        setupClickListeners()
+    }
+
+    private fun setupViewModel() {
         val factory = TransactionViewModelFactory(repository)
-        val transactionViewModel = ViewModelProvider(this, factory)[TransactionViewModel::class.java]
+        transactionViewModel = ViewModelProvider(this, factory)[TransactionViewModel::class.java]
 
-        _binding = FragmentTransactionBinding.inflate(inflater, container, false)
+        transactionViewModel.listTransactions.observe(viewLifecycleOwner) { transactionList ->
+            transactionAdapter.setTransactionList(transactionList)
+        }
+    }
+
+    private fun setupRecyclerView() {
+        transactionAdapter = TransactionAdapter(transactionViewModel)
+        binding.transactionList.apply {
+            layoutManager = LinearLayoutManager(context)
+            adapter = transactionAdapter
+        }
+    }
 
-        binding.button.setOnClickListener {
+    private fun setupClickListeners() {
+        binding.addTransactionButton.setOnClickListener {
             val intent = Intent(activity, AddTransactionActivity::class.java)
             startActivity(intent)
         }
-
-        return binding.root
     }
 
     override fun onDestroyView() {
diff --git a/app/src/main/java/com/onionsquad/bondoman/ui/transaction/TransactionViewModel.kt b/app/src/main/java/com/onionsquad/bondoman/ui/transaction/TransactionViewModel.kt
index cf96283f3289be47bfd3205f7e6faa0ac4467748..e34a11eb51d9dcf434b68e8089586dab0911a7a2 100644
--- a/app/src/main/java/com/onionsquad/bondoman/ui/transaction/TransactionViewModel.kt
+++ b/app/src/main/java/com/onionsquad/bondoman/ui/transaction/TransactionViewModel.kt
@@ -21,4 +21,8 @@ class TransactionViewModel(private val repository : TransactionRepository) : Vie
     fun deleteTransaction(transaction: TransactionEntity) = viewModelScope.launch {
         repository.deleteTransaction(transaction)
     }
+
+    fun getTransactionById(id: Int): LiveData<TransactionEntity> {
+        return repository.getTransactionById(id)
+    }
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/util/Converters.kt b/app/src/main/java/com/onionsquad/bondoman/util/Converters.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ce6beddb309958907a9a4f30e9e9f53511b04ac0
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/util/Converters.kt
@@ -0,0 +1,34 @@
+package com.onionsquad.bondoman.util
+
+import androidx.room.TypeConverter
+import com.onionsquad.bondoman.room.TransactionCategory
+import java.util.Date
+
+object Converters {
+
+    @TypeConverter
+    @JvmStatic
+    fun fromTimestamp(value: Long?): Date? {
+        return value?.let { Date(it) }
+    }
+
+    @TypeConverter
+    @JvmStatic
+    fun dateToTimestamp(value: Date?): Long? {
+        return value?.time
+    }
+
+    @TypeConverter
+    fun fromTransactionCategory(category: TransactionCategory): String {
+        return category.name
+    }
+
+    @TypeConverter
+    fun toTransactionCategory(categoryString: String): TransactionCategory {
+        return try {
+            TransactionCategory.valueOf(categoryString)
+        } catch (e: IllegalArgumentException) {
+            TransactionCategory.UNKNOWN
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/util/DateConverter.kt b/app/src/main/java/com/onionsquad/bondoman/util/DateConverter.kt
deleted file mode 100644
index 408d43f29d0fef1feb655b043a8080594af926e5..0000000000000000000000000000000000000000
--- a/app/src/main/java/com/onionsquad/bondoman/util/DateConverter.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.onionsquad.bondoman.util
-
-import androidx.room.TypeConverter
-import java.util.Date
-
-object DateConverter {
-
-    @TypeConverter
-    @JvmStatic
-    fun fromTimestamp(value: Long?): Date? {
-        return value?.let { Date(it) }
-    }
-
-    @TypeConverter
-    @JvmStatic
-    fun dateToTimestamp(value: Date?): Long? {
-        return value?.time
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/card_background.xml b/app/src/main/res/drawable/card_background.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9687bcb49124a23fdc528e7bcfc09da50ff742f0
--- /dev/null
+++ b/app/src/main/res/drawable/card_background.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="#FFBB86FC" />
+    <corners android:radius="8dp" />
+</shape>
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_add.xml b/app/src/main/res/drawable/ic_add.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8140012280ccf471ae3e11e44058ebfb0a4fa6e4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_add.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
+</vector>
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_baseline_email_24.xml b/app/src/main/res/drawable/ic_baseline_email_24.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a3335d40f9e0e89792c5336c7964bc79e145aa04
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_email_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="M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM20,8l-8,5 -8,-5L4,6l8,5 8,-5v2z"/>
+    
+</vector>
diff --git a/app/src/main/res/drawable/ic_baseline_logout_24.xml b/app/src/main/res/drawable/ic_baseline_logout_24.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c22a96f911c6a8c7615c8e149ee796eddc23d449
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_logout_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="M17,7l-1.41,1.41L18.17,11H8v2h10.17l-2.58,2.58L17,17l5,-5zM4,5h8V3H4c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h8v-2H4V5z"/>
+    
+</vector>
diff --git a/app/src/main/res/drawable/ic_baseline_save_24.xml b/app/src/main/res/drawable/ic_baseline_save_24.xml
new file mode 100644
index 0000000000000000000000000000000000000000..cfd40f5f8e0996658b48908b447dc9886b8ec601
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_save_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="M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM12,19c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM15,9L5,9L5,5h10v4z"/>
+    
+</vector>
diff --git a/app/src/main/res/drawable/ic_baseline_signal_wifi_connected_no_internet_4_24.xml b/app/src/main/res/drawable/ic_baseline_signal_wifi_connected_no_internet_4_24.xml
new file mode 100644
index 0000000000000000000000000000000000000000..49da62c2a8894234fe34497c062850789200b223
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_signal_wifi_connected_no_internet_4_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="M24,8.98C20.93,5.9 16.69,4 12,4C7.31,4 3.07,5.9 0,8.98L12,21v-9h8.99L24,8.98zM19.59,14l-2.09,2.09L15.41,14L14,15.41l2.09,2.09L14,19.59L15.41,21l2.09,-2.08L19.59,21L21,19.59l-2.08,-2.09L21,15.41L19.59,14z"/>
+    
+</vector>
diff --git a/app/src/main/res/drawable/ic_delete.xml b/app/src/main/res/drawable/ic_delete.xml
new file mode 100644
index 0000000000000000000000000000000000000000..618a75853cf62efae9734d66d4e3cdd2396fd080
--- /dev/null
+++ b/app/src/main/res/drawable/ic_delete.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <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>
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_edit.xml b/app/src/main/res/drawable/ic_edit.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3ea930d89592aa614ceb09cefe6ee449dc55f1da
--- /dev/null
+++ b/app/src/main/res/drawable/ic_edit.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <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>
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_add_transaction.xml b/app/src/main/res/layout/activity_add_transaction.xml
index 1858a85b38c01dfd8eda38a6101a69ec026fd3a5..95fa2b3af87c278d7b251600cecf1c1b0d0ddacb 100644
--- a/app/src/main/res/layout/activity_add_transaction.xml
+++ b/app/src/main/res/layout/activity_add_transaction.xml
@@ -28,13 +28,26 @@
         android:hint="Nominal"
         android:inputType="numberDecimal" />
 
-    <EditText
-        android:id="@+id/categoryEditText"
+    <RadioGroup
+        android:id="@+id/categoryRadioGroup"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginTop="8dp"
-        android:hint="Kategori"
-        android:inputType="text" />
+        android:orientation="horizontal">
+
+        <RadioButton
+            android:id="@+id/incomeRadioButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Pemasukan" />
+
+        <RadioButton
+            android:id="@+id/outcomeRadioButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Pengeluaran" />
+
+    </RadioGroup>
 
     <EditText
         android:id="@+id/locationEditText"
diff --git a/app/src/main/res/layout/activity_edit_transaction.xml b/app/src/main/res/layout/activity_edit_transaction.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8b23b5075a31acd86cc49cdd54a73576f5b219d7
--- /dev/null
+++ b/app/src/main/res/layout/activity_edit_transaction.xml
@@ -0,0 +1,66 @@
+<?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:orientation="vertical"
+    android:padding="16dp">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Edit Transaksi"
+        android:textSize="24sp"
+        android:textStyle="bold" />
+
+    <EditText
+        android:id="@+id/titleEditText"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:hint="Judul"
+        android:inputType="text" />
+
+    <EditText
+        android:id="@+id/amountEditText"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="8dp"
+        android:hint="Nominal"
+        android:inputType="numberDecimal" />
+
+    <RadioGroup
+        android:id="@+id/categoryRadioGroup"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="8dp"
+        android:orientation="horizontal">
+
+        <RadioButton
+            android:id="@+id/incomeRadioButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Pemasukan" />
+
+        <RadioButton
+            android:id="@+id/outcomeRadioButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Pengeluaran" />
+    </RadioGroup>
+
+    <EditText
+        android:id="@+id/locationEditText"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="8dp"
+        android:hint="Lokasi"
+        android:inputType="text" />
+
+    <Button
+        android:id="@+id/saveButton"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:text="Simpan" />
+
+</LinearLayout>
\ 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..934a2edc6b2b4eacc44b02b986b6628f51873712
--- /dev/null
+++ b/app/src/main/res/layout/activity_login.xml
@@ -0,0 +1,71 @@
+<?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/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    tools:context=".ui.login.LoginActivity">
+
+    <EditText
+        android:id="@+id/username"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="96dp"
+        android:autofillHints="@string/prompt_email"
+        android:hint="@string/prompt_email"
+        android:inputType="textEmailAddress"
+        android:selectAllOnFocus="true"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <EditText
+        android:id="@+id/password"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="8dp"
+        android:autofillHints="@string/prompt_password"
+        android:hint="@string/prompt_password"
+        android:imeActionLabel="@string/action_sign_in"
+        android:imeOptions="actionDone"
+        android:inputType="textPassword"
+        android:selectAllOnFocus="true"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/username" />
+
+    <Button
+        android:id="@+id/login"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="start"
+        android:layout_marginTop="16dp"
+        android:layout_marginBottom="64dp"
+        android:enabled="false"
+        android:text="@string/action_sign_in"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/password"
+        app:layout_constraintVertical_bias="0.2" />
+
+    <ProgressBar
+        android:id="@+id/loading"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_marginTop="64dp"
+        android:layout_marginBottom="64dp"
+        android:visibility="gone"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="@+id/password"
+        app:layout_constraintStart_toStartOf="@+id/password"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_bias="0.3" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_no_internet.xml b/app/src/main/res/layout/activity_no_internet.xml
new file mode 100644
index 0000000000000000000000000000000000000000..83fadbcdb8ddb17ee2e949e3f0c4259e159de028
--- /dev/null
+++ b/app/src/main/res/layout/activity_no_internet.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout 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">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:layout_centerHorizontal="true"
+        android:layout_centerVertical="true"
+        tools:layout_editor_absoluteX="1dp"
+        tools:layout_editor_absoluteY="1dp" >
+
+        <ImageView
+            android:id="@+id/no_internet_image"
+            android:layout_width="match_parent"
+            android:layout_height="160dp"
+            android:alpha="0.2"
+            android:contentDescription="@string/no_internet"
+            app:srcCompat="@drawable/ic_baseline_signal_wifi_connected_no_internet_4_24" />
+
+        <TextView
+            android:id="@+id/text_no_internet"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textSize="30sp"
+            android:textAlignment="center"
+            android:alpha="0.2"
+            android:text="@string/no_internet" />
+    </LinearLayout>
+</RelativeLayout>
\ 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
index 441476f744f7bedaef0590d7b4b632a82ee0354d..b4fa8f9139a6487708df28d49918d4d29402779e 100644
--- a/app/src/main/res/layout/fragment_settings.xml
+++ b/app/src/main/res/layout/fragment_settings.xml
@@ -1,8 +1,53 @@
 <?xml version="1.0" encoding="utf-8"?>
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:orientation="vertical"
     tools:context=".ui.settings.SettingsFragment">
 
-</FrameLayout>
\ No newline at end of file
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <Button
+            android:id="@+id/button_save"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="0.7"
+            android:backgroundTint="@color/white"
+            android:drawableLeft="@drawable/ic_baseline_save_24"
+            android:text="@string/action_save_transactions"
+            android:textAlignment="textStart"
+            android:textColor="@color/black" />
+
+        <Spinner
+            android:id="@+id/spinner_excel_type"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="0.3" />
+    </LinearLayout>
+
+    <Button
+        android:id="@+id/button_send_email"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:backgroundTint="@color/white"
+        android:drawableLeft="@drawable/ic_baseline_email_24"
+        android:text="@string/action_save_email"
+        android:textAlignment="textStart"
+        android:textColor="@color/black" />
+
+    <Button
+        android:id="@+id/button_logout"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:backgroundTint="@color/white"
+        android:drawableLeft="@drawable/ic_baseline_logout_24"
+        android:drawableTint="@color/design_default_color_error"
+        android:text="@string/action_sign_out"
+        android:textAlignment="textStart"
+        android:textColor="@color/design_default_color_error" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_transaction.xml b/app/src/main/res/layout/fragment_transaction.xml
index 26af3dd3316b3d39d8d4a93547724bcb95a87311..f6bdd20ec3dd92fcc5f68be9c57f83d2e5c47bb5 100644
--- a/app/src/main/res/layout/fragment_transaction.xml
+++ b/app/src/main/res/layout/fragment_transaction.xml
@@ -6,15 +6,25 @@
     android:layout_height="match_parent"
     tools:context=".ui.transaction.TransactionFragment">
 
-    <Button
-        android:id="@+id/button"
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/transactionList"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:listitem="@layout/transaction_card" />
+
+    <com.google.android.material.floatingactionbutton.FloatingActionButton
+        android:id="@+id/addTransactionButton"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:text="Button"
-        android:visibility="visible"
+        android:layout_margin="16dp"
+        android:src="@drawable/ic_add"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent" />
+        app:backgroundTint="#FF6200EE" />
 
 </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/transaction_card.xml b/app/src/main/res/layout/transaction_card.xml
new file mode 100644
index 0000000000000000000000000000000000000000..0460ea905c63c1d90b81d1d5b48e56429e263623
--- /dev/null
+++ b/app/src/main/res/layout/transaction_card.xml
@@ -0,0 +1,96 @@
+<?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"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_margin="10dp"
+    app:cardCornerRadius="8dp"
+    app:cardElevation="4dp">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@drawable/card_background"
+        android:padding="16dp">
+
+        <TextView
+            android:id="@+id/dateTextView"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="16sp"
+            android:textStyle="bold"
+            android:textColor="#FFFFFF"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            tools:text="29/02/2024" />
+
+        <TextView
+            android:id="@+id/categoryTextView"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="8dp"
+            android:textSize="14sp"
+            android:textColor="#FFFFFF"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/dateTextView"
+            tools:text="Pembelian" />
+
+        <TextView
+            android:id="@+id/titleTextView"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="18sp"
+            android:textStyle="bold"
+            android:textColor="#FFFFFF"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/categoryTextView"
+            tools:text="Transaction Name" />
+
+        <TextView
+            android:id="@+id/amountTextView"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="16sp"
+            android:textColor="#FFFFFF"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            tools:text="IDR 15.000" />
+
+        <TextView
+            android:id="@+id/locationTextView"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="14sp"
+            android:textColor="#FFFFFF"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/amountTextView"
+            tools:text="Location" />
+
+        <ImageButton
+            android:id="@+id/editButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="8dp"
+            android:layout_marginEnd="10dp"
+            android:background="?attr/selectableItemBackgroundBorderless"
+            android:src="@drawable/ic_edit"
+            app:layout_constraintEnd_toStartOf="@+id/deleteButton"
+            app:layout_constraintTop_toBottomOf="@id/titleTextView"
+            app:tint="#FFFFFF" />
+
+        <ImageButton
+            android:id="@+id/deleteButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="8dp"
+            android:layout_marginTop="8dp"
+            android:background="?attr/selectableItemBackgroundBorderless"
+            android:src="@drawable/ic_delete"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/titleTextView"
+            app:tint="#FFFFFF" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</androidx.cardview.widget.CardView>
\ No newline at end of file
diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml
index 56c03a8376f28c9b6734087a3ff220d5f679beeb..e583f294812112bb767d715ad02a0e68165ac49a 100644
--- a/app/src/main/res/values-night/themes.xml
+++ b/app/src/main/res/values-night/themes.xml
@@ -13,4 +13,9 @@
         <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
         <!-- Customize your theme here. -->
     </style>
+  <!-- Base application theme. -->
+    <style name="Base.Theme.Bondoman" parent="Theme.Material3.DayNight.NoActionBar">
+        <!-- Customize your dark theme here. -->
+        <!-- <item name="colorPrimary">@color/my_dark_primary</item> -->
+    </style>
 </resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
new file mode 100644
index 0000000000000000000000000000000000000000..df459588e60132333d50587f1090784e62693410
--- /dev/null
+++ b/app/src/main/res/values/arrays.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string-array name="excel_types">
+        <item>XLS</item>
+        <item>XLSX</item>
+    </string-array>
+</resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index f49468903d6e80e80d57478229c158ae05fd7b11..5c9f62875de7081ca2d6d39939abc9c1eee84cba 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -3,4 +3,5 @@
     <dimen name="activity_horizontal_margin">16dp</dimen>
     <dimen name="activity_vertical_margin">16dp</dimen>
     <dimen name="graph_no_data_text">20sp</dimen>
+    <dimen name="fab_margin">16dp</dimen>
 </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 8d737aefdf31b4dc1aebb0a6abcc7082e2efa1e9..41677d694a5d90740d494a11f048bb3cecb681ff 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -5,4 +5,66 @@
     <string name="title_graph">Graph</string>
     <string name="title_settings">Settings</string>
     <string name="graph_no_data">No data</string>
+    <string name="title_activity_login">Sign in</string>
+    <string name="prompt_email">Email</string>
+    <string name="prompt_password">Password</string>
+    <string name="action_sign_in">Sign in</string>
+    <string name="login_success">"Login success!"</string>
+    <string name="invalid_email">Not a valid email</string>
+    <string name="login_failed">"Login failed"</string>
+    <string name="preference_file_key">BondomanSharedPrefs</string>
+    <string name="action_sign_out">Sign out</string>
+    <string name="title_alert_logout">Confirm sign out</string>
+    <string name="message_alert_logout">Are you sure you want to sign out?</string>
+    <string name="log_out_success">Log out success</string>
+    <string name="yes">Yes</string>
+    <string name="no">No</string>
+    <string name="action_save_transactions">Save transaction</string>
+    <string name="action_save_email">Send transactions</string>
+    <string name="transactions_saved">Transactions saved</string>
+    <string name="transactions_sent">Transactions sent to email</string>
+    <string name="internet_not_connected">You are not connecting to internet</string>
+    <!-- Strings used for fragments for navigation -->
+    <string name="first_fragment_label">First Fragment</string>
+    <string name="second_fragment_label">Second Fragment</string>
+    <string name="next">Next</string>
+    <string name="previous">Previous</string>
+
+    <string name="lorem_ipsum">
+        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam in scelerisque sem. Mauris
+        volutpat, dolor id interdum ullamcorper, risus dolor egestas lectus, sit amet mattis purus
+        dui nec risus. Maecenas non sodales nisi, vel dictum dolor. Class aptent taciti sociosqu ad
+        litora torquent per conubia nostra, per inceptos himenaeos. Suspendisse blandit eleifend
+        diam, vel rutrum tellus vulputate quis. Aliquam eget libero aliquet, imperdiet nisl a,
+        ornare ex. Sed rhoncus est ut libero porta lobortis. Fusce in dictum tellus.\n\n
+        Suspendisse interdum ornare ante. Aliquam nec cursus lorem. Morbi id magna felis. Vivamus
+        egestas, est a condimentum egestas, turpis nisl iaculis ipsum, in dictum tellus dolor sed
+        neque. Morbi tellus erat, dapibus ut sem a, iaculis tincidunt dui. Interdum et malesuada
+        fames ac ante ipsum primis in faucibus. Curabitur et eros porttitor, ultricies urna vitae,
+        molestie nibh. Phasellus at commodo eros, non aliquet metus. Sed maximus nisl nec dolor
+        bibendum, vel congue leo egestas.\n\n
+        Sed interdum tortor nibh, in sagittis risus mollis quis. Curabitur mi odio, condimentum sit
+        amet auctor at, mollis non turpis. Nullam pretium libero vestibulum, finibus orci vel,
+        molestie quam. Fusce blandit tincidunt nulla, quis sollicitudin libero facilisis et. Integer
+        interdum nunc ligula, et fermentum metus hendrerit id. Vestibulum lectus felis, dictum at
+        lacinia sit amet, tristique id quam. Cras eu consequat dui. Suspendisse sodales nunc ligula,
+        in lobortis sem porta sed. Integer id ultrices magna, in luctus elit. Sed a pellentesque
+        est.\n\n
+        Aenean nunc velit, lacinia sed dolor sed, ultrices viverra nulla. Etiam a venenatis nibh.
+        Morbi laoreet, tortor sed facilisis varius, nibh orci rhoncus nulla, id elementum leo dui
+        non lorem. Nam mollis ipsum quis auctor varius. Quisque elementum eu libero sed commodo. In
+        eros nisl, imperdiet vel imperdiet et, scelerisque a mauris. Pellentesque varius ex nunc,
+        quis imperdiet eros placerat ac. Duis finibus orci et est auctor tincidunt. Sed non viverra
+        ipsum. Nunc quis augue egestas, cursus lorem at, molestie sem. Morbi a consectetur ipsum, a
+        placerat diam. Etiam vulputate dignissim convallis. Integer faucibus mauris sit amet finibus
+        convallis.\n\n
+        Phasellus in aliquet mi. Pellentesque habitant morbi tristique senectus et netus et
+        malesuada fames ac turpis egestas. In volutpat arcu ut felis sagittis, in finibus massa
+        gravida. Pellentesque id tellus orci. Integer dictum, lorem sed efficitur ullamcorper,
+        libero justo consectetur ipsum, in mollis nisl ex sed nisl. Donec maximus ullamcorper
+        sodales. Praesent bibendum rhoncus tellus nec feugiat. In a ornare nulla. Donec rhoncus
+        libero vel nunc consequat, quis tincidunt nisl eleifend. Cras bibendum enim a justo luctus
+        vestibulum. Fusce dictum libero quis erat maximus, vitae volutpat diam dignissim.
+    </string>
+    <string name="no_internet">No internet</string>
 </resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index c39849b812b4a960243b412e67ae6101d43e244f..6d562401242a5cbb0c942e57f091934d7c7f4796 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -13,4 +13,9 @@
         <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
         <!-- Customize your theme here. -->
     </style>
+  <!-- Base application theme. -->
+    <style name="Base.Theme.Bondoman" parent="Theme.Material3.DayNight.NoActionBar">
+        <!-- Customize your light theme here. -->
+        <!-- <item name="colorPrimary">@color/my_light_primary</item> -->
+    </style>
 </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..0a9df4b573a6caf941e16d0eb5b2f8cee1ce5cea
--- /dev/null
+++ b/app/src/main/res/xml/file_paths.xml
@@ -0,0 +1,3 @@
+<paths xmlns:android="http://schemas.android.com/apk/res/android">
+    <cache-path name="cache" path="/" />
+</paths>
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 10345b9864bd86655501aca6c855f98b780b82f1..214babfbd91ed3d7211cbe30f42207a6fbea57ed 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -16,6 +16,11 @@ room = "2.6.1"
 kapt = "1.9.23"
 lifecycleViewmodelCompose = "2.7.0"
 chart = "v3.1.0"
+retrofit = "2.11.0"
+okhttp3 = "4.12.0"
+annotation = "1.7.1"
+work = "2.9.0"
+excelkt = "1.0.2"
 
 [libraries]
 androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@@ -34,7 +39,17 @@ room-compiler = { group = "androidx.room", name = "room-compiler", version.ref =
 room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
 room-common = { group = "androidx.room", name = "room-common", version.ref = "room" }
 androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycleViewmodelCompose" }
+<<<<<<< gradle/libs.versions.toml
 mpandroidchart = { group = "com.github.PhilJay", name = "MPAndroidChart", version.ref = "chart" }
+=======
+retrofit2-retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
+retrofit2-converter-scalars = { group = "com.squareup.retrofit2", name = "converter-scalars", version.ref = "retrofit" }
+retrofit2-converter-gson = { group = "com.squareup.retrofit2", name = "converter-gson", version.ref = "retrofit" }
+okhttp3 = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp3"  }
+androidx-annotation = { group = "androidx.annotation", name = "annotation", version.ref = "annotation" }
+androidx-work-runtime = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "work" }
+excelkt = { group = "io.github.evanrupert", name = "excelkt", version.ref = "excelkt" }
+>>>>>>> gradle/libs.versions.toml
 
 [plugins]
 androidApplication = { id = "com.android.application", version.ref = "agp" }