From 94e7b5c825c8e8bd6c3c590f172ca4d8c775d525 Mon Sep 17 00:00:00 2001
From: RyanSC06 <cadanganryansc@gmail.com>
Date: Sat, 30 Mar 2024 23:28:33 +0700
Subject: [PATCH] feat: transactions

---
 .idea/gradle.xml                              |   1 +
 .idea/vcs.xml                                 |   6 +
 app/.idea/.gitignore                          |   3 +
 app/.idea/gradle.xml                          |  12 ++
 app/.idea/migrations.xml                      |  10 ++
 app/.idea/misc.xml                            |  10 ++
 app/.idea/vcs.xml                             |   6 +
 app/build.gradle.kts                          |   5 +
 app/src/.idea/.gitignore                      |   3 +
 app/src/.idea/misc.xml                        |   6 +
 app/src/.idea/modules.xml                     |  11 ++
 app/src/.idea/vcs.xml                         |   6 +
 .../myapplication/AddTransactionActivity.kt   | 111 ++++++++++++++++++
 .../com/example/myapplication/MainActivity.kt |   2 +-
 .../myapplication/dao/TransactionDao.kt       |  22 ++++
 .../database/TransactionDatabase.kt           |  19 +++
 .../entities/TransactionEntities.kt           |  27 +++++
 .../repository/TransactionRepository.kt       |  12 ++
 .../ui/transaction/TransactionsFragment.kt    |  45 +++++++
 .../res/layout/activity_transaction_list.xml  |  11 ++
 app/src/main/res/layout/item_transaction.xml  |  38 ++++++
 .../main/res/navigation/mobile_navigation.xml |   7 ++
 gradle/wrapper/gradle-wrapper.properties      |   4 +-
 23 files changed, 374 insertions(+), 3 deletions(-)
 create mode 100644 .idea/vcs.xml
 create mode 100644 app/.idea/.gitignore
 create mode 100644 app/.idea/gradle.xml
 create mode 100644 app/.idea/migrations.xml
 create mode 100644 app/.idea/misc.xml
 create mode 100644 app/.idea/vcs.xml
 create mode 100644 app/src/.idea/.gitignore
 create mode 100644 app/src/.idea/misc.xml
 create mode 100644 app/src/.idea/modules.xml
 create mode 100644 app/src/.idea/vcs.xml
 create mode 100644 app/src/main/java/com/example/myapplication/AddTransactionActivity.kt
 create mode 100644 app/src/main/java/com/example/myapplication/dao/TransactionDao.kt
 create mode 100644 app/src/main/java/com/example/myapplication/database/TransactionDatabase.kt
 create mode 100644 app/src/main/java/com/example/myapplication/entities/TransactionEntities.kt
 create mode 100644 app/src/main/java/com/example/myapplication/repository/TransactionRepository.kt
 create mode 100644 app/src/main/java/com/example/myapplication/ui/transaction/TransactionsFragment.kt
 create mode 100644 app/src/main/res/layout/activity_transaction_list.xml
 create mode 100644 app/src/main/res/layout/item_transaction.xml

diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 0897082..d12ae9e 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -15,5 +15,6 @@
         <option name="resolveExternalAnnotations" value="false" />
       </GradleProjectSettings>
     </option>
+    <option name="offlineMode" value="true" />
   </component>
 </project>
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="" vcs="Git" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/app/.idea/.gitignore b/app/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/app/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/app/.idea/gradle.xml b/app/.idea/gradle.xml
new file mode 100644
index 0000000..89935b5
--- /dev/null
+++ b/app/.idea/gradle.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="GradleSettings">
+    <option name="linkedExternalProjectsSettings">
+      <GradleProjectSettings>
+        <option name="externalProjectPath" value="$PROJECT_DIR$" />
+        <option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
+        <option name="resolveExternalAnnotations" value="false" />
+      </GradleProjectSettings>
+    </option>
+  </component>
+</project>
\ No newline at end of file
diff --git a/app/.idea/migrations.xml b/app/.idea/migrations.xml
new file mode 100644
index 0000000..f8051a6
--- /dev/null
+++ b/app/.idea/migrations.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectMigrations">
+    <option name="MigrateToGradleLocalJavaHome">
+      <set>
+        <option value="$PROJECT_DIR$" />
+      </set>
+    </option>
+  </component>
+</project>
\ No newline at end of file
diff --git a/app/.idea/misc.xml b/app/.idea/misc.xml
new file mode 100644
index 0000000..3040d03
--- /dev/null
+++ b/app/.idea/misc.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ExternalStorageConfigurationManager" enabled="true" />
+  <component name="ProjectRootManager">
+    <output url="file://$PROJECT_DIR$/build/classes" />
+  </component>
+  <component name="ProjectType">
+    <option name="id" value="Android" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/app/.idea/vcs.xml b/app/.idea/vcs.xml
new file mode 100644
index 0000000..6c0b863
--- /dev/null
+++ b/app/.idea/vcs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$/.." vcs="Git" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 019d126..bfffbf2 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -54,4 +54,9 @@ dependencies {
     testImplementation(libs.junit)
     androidTestImplementation(libs.androidx.junit)
     androidTestImplementation(libs.androidx.espresso.core)
+
+    implementation "androidx.room:room-runtime:2.4.0"
+    kapt "androidx.room:room-compiler:2.4.0"
+    implementation "androidx.room:room-ktx:2.4.0"
+    testImplementation "androidx.room:room-testing:2.4.0"
 }
\ No newline at end of file
diff --git a/app/src/.idea/.gitignore b/app/src/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/app/src/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/app/src/.idea/misc.xml b/app/src/.idea/misc.xml
new file mode 100644
index 0000000..639900d
--- /dev/null
+++ b/app/src/.idea/misc.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectRootManager">
+    <output url="file://$PROJECT_DIR$/out" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/app/src/.idea/modules.xml b/app/src/.idea/modules.xml
new file mode 100644
index 0000000..403a512
--- /dev/null
+++ b/app/src/.idea/modules.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/androidTest/androidTest.iml" filepath="$PROJECT_DIR$/androidTest/androidTest.iml" />
+      <module fileurl="file://$PROJECT_DIR$/main/main.iml" filepath="$PROJECT_DIR$/main/main.iml" />
+      <module fileurl="file://$PROJECT_DIR$/.idea/src.iml" filepath="$PROJECT_DIR$/.idea/src.iml" />
+      <module fileurl="file://$PROJECT_DIR$/test/test.iml" filepath="$PROJECT_DIR$/test/test.iml" />
+    </modules>
+  </component>
+</project>
\ No newline at end of file
diff --git a/app/src/.idea/vcs.xml b/app/src/.idea/vcs.xml
new file mode 100644
index 0000000..b2bdec2
--- /dev/null
+++ b/app/src/.idea/vcs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/AddTransactionActivity.kt b/app/src/main/java/com/example/myapplication/AddTransactionActivity.kt
new file mode 100644
index 0000000..293d674
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/AddTransactionActivity.kt
@@ -0,0 +1,111 @@
+import androidx.appcompat.app.AppCompatActivity
+import android.os.Bundle
+import android.widget.Toast
+import androidx.lifecycle.ViewModelProvider
+import java.util.Date
+
+
+class AddTransactionActivity : AppCompatActivity() {
+    private lateinit var titleEditText: EditText
+    private lateinit var nominalEditText: EditText
+    private lateinit var kategoriEditText: EditText
+    private lateinit var lokasiEditText: EditText
+    private lateinit var saveButton: Button
+
+    private lateinit var transactionViewModel: TransactionViewModel
+    private lateinit var transactionRepository: TransactionRepository
+
+    private var currentLocation: Location? = null
+
+    companion object {
+        private const val LOCATION_PERMISSION_REQUEST_CODE = 123
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_add_transaction)
+
+        titleEditText = findViewById(R.id.editTextJudul)
+        nominalEditText = findViewById(R.id.editTextNominal)
+        kategoriEditText = findViewById(R.id.editTextKategori)
+        lokasiEditText = findViewById(R.id.editTextLokasi)
+        saveButton = findViewById(R.id.buttonSimpan)
+
+        val transactionDao = TransactionDatabase.getInstance(this).transactionDao()
+        transactionRepository = TransactionRepository(transactionDao)
+        transactionViewModel = ViewModelProvider(this).get(TransactionViewModel::class.java)
+        transactionViewModel.initialize(transactionRepository)
+
+        saveButton.setOnClickListener { saveTransaction() }
+        getLocation()
+    }
+
+    private fun saveTransaction() {
+        val title = titleEditText.text.toString().trim()
+        val nominal = nominalEditText.text.toString().trim()
+        val kategori = kategoriEditText.text.toString().trim()
+        val lokasi = lokasiEditText.text.toString().trim()
+        val tanggal = Date()
+
+        if (title.isEmpty() || nominal.isEmpty() || kategori.isEmpty() || lokasi.isEmpty()) {
+            Toast.makeText(this, "Isi semua atribut transaksi", Toast.LENGTH_SHORT).show()
+            return
+        }
+
+        val transaction = TransactionEntity(title, nominal, kategori, lokasi, tanggal)
+        transactionViewModel.addTransaction(transaction)
+        Toast.makeText(this, "Transaksi berhasil ditambahkan", Toast.LENGTH_SHORT).show()
+        finish()
+    }
+
+    
+    private fun saveTransaction() {
+        val title = titleEditText.text.toString().trim()
+        val nominal = nominalEditText.text.toString().trim()
+        val kategori = kategoriEditText.text.toString().trim()
+        val lokasi = lokasiEditText.text.toString().trim()
+        val tanggal = Date()
+
+        if (title.isEmpty() || nominal.isEmpty() || kategori.isEmpty() || lokasi.isEmpty()) {
+            Toast.makeText(this, "Isi semua atribut transaksi", Toast.LENGTH_SHORT).show()
+            return
+        }
+
+        val transaction = TransactionEntity(title = title, nominal = nominal, kategori = kategori, lokasi = lokasi, tanggal = tanggal)
+        val insertedTransactionId = transactionViewModel.addTransaction(transaction)
+        Toast.makeText(this, "Transaksi berhasil ditambahkan dengan ID $insertedTransactionId", Toast.LENGTH_SHORT).show()
+        finish()
+    }
+
+    
+    @SuppressLint("MissingPermission")
+    private fun getLocation() {
+        if (LocationUtils.isLocationPermissionGranted(this)) {
+            LocationUtils.requestLocationUpdates(this) { location ->
+                currentLocation = location
+                lokasiEditText.setText("${location.latitude}, ${location.longitude}")
+            }
+        } else {
+            ActivityCompat.requestPermissions(
+                this,
+                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
+                LOCATION_PERMISSION_REQUEST_CODE
+            )
+        }
+    }
+
+    override fun onRequestPermissionsResult(
+        requestCode: Int,
+        permissions: Array<out String>,
+        grantResults: IntArray
+    ) {
+        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+        if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) {
+            if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                getLocation()
+            } else {
+                Toast.makeText(this, "Izin akses lokasi ditolak", Toast.LENGTH_SHORT).show()
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/MainActivity.kt b/app/src/main/java/com/example/myapplication/MainActivity.kt
index 4d6ef5d..d3fe50c 100644
--- a/app/src/main/java/com/example/myapplication/MainActivity.kt
+++ b/app/src/main/java/com/example/myapplication/MainActivity.kt
@@ -32,7 +32,7 @@ class MainActivity : AppCompatActivity() {
         // menu should be considered as top level destinations.
         val appBarConfiguration = AppBarConfiguration(
             setOf(
-                R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications
+                R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications, R.id.navigation_transactions
             )
         )
         setupActionBarWithNavController(navController, appBarConfiguration)
diff --git a/app/src/main/java/com/example/myapplication/dao/TransactionDao.kt b/app/src/main/java/com/example/myapplication/dao/TransactionDao.kt
new file mode 100644
index 0000000..2decc8c
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/dao/TransactionDao.kt
@@ -0,0 +1,22 @@
+import androidx.lifecycle.LiveData
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.Query
+import androidx.room.Update
+import androidx.room.Delete
+
+
+@Dao
+interface TransactionDao {
+    @Insert
+    suspend fun insertTransaction(transaction: TransactionEntity): Long
+
+    @Query("SELECT * FROM transactionentity")
+    fun getAllTransactions(): LiveData<List<TransactionEntity>>
+
+    @Update
+    fun update(transaction: TransactionEntity)
+
+    @Delete
+    fun delete(transaction: TransactionEntity)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/database/TransactionDatabase.kt b/app/src/main/java/com/example/myapplication/database/TransactionDatabase.kt
new file mode 100644
index 0000000..7785998
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/database/TransactionDatabase.kt
@@ -0,0 +1,19 @@
+@Database (entities = [TransactionEntity::class], version = 1)
+abstract class TransactionDatabase : RoomDatabase() {
+    abstract fun transactionDao(): TransactionDao
+
+    companion object {
+        @Volatile
+        private var instance: TransactionDatabase? = null
+
+        fun getInstance (context: Context): TransactionDatabase {
+            return instance ?: synchronized(this) {
+                instance ?: Room.databaseBuilder(
+                    context.applicationContext,
+                    TransactionDatabase::class.java,
+                    "transaction_database"
+                ).build().also { instance = it }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/entities/TransactionEntities.kt b/app/src/main/java/com/example/myapplication/entities/TransactionEntities.kt
new file mode 100644
index 0000000..9455a4e
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/entities/TransactionEntities.kt
@@ -0,0 +1,27 @@
+import java.util.Date
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import androidx.room.ColumnInfo
+
+
+@Entity(tableName = "transactionentity")
+data class TransactionEntity(
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "id")
+    val id: Int = 0,
+
+    @ColumnInfo(name = "title")
+    val title: String,
+
+    @ColumnInfo(name = "nominal")
+    val nominal: String,
+
+    @ColumnInfo(name = "kategori")
+    val kategori: String,
+
+    @ColumnInfo(name = "lokasi")
+    val lokasi: String,
+
+    @ColumnInfo(name = "tanggal")
+    val tanggal: Date
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/repository/TransactionRepository.kt b/app/src/main/java/com/example/myapplication/repository/TransactionRepository.kt
new file mode 100644
index 0000000..e8d6cd2
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/repository/TransactionRepository.kt
@@ -0,0 +1,12 @@
+import androidx.lifecycle.LiveData
+import androidx.room.Update
+import androidx.room.Delete
+
+
+class TransactionRepository(private val transactionDao: TransactionDao) {
+    val allTransactions: LiveData<List<TransactionEntity>> = transactionDao.getAllTransactions()
+
+    suspend fun insertTransaction(transaction: TransactionEntity): Long {
+        return transactionDao.insertTransaction(transaction)
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/ui/transaction/TransactionsFragment.kt b/app/src/main/java/com/example/myapplication/ui/transaction/TransactionsFragment.kt
new file mode 100644
index 0000000..f3cd330
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/ui/transaction/TransactionsFragment.kt
@@ -0,0 +1,45 @@
+package com.example.myapplication.ui.transactions
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.example.myapplication.R
+import com.example.myapplication.data.Transaction
+import com.example.myapplication.data.TransactionAdapter
+
+class TransactionsFragment : Fragment() {
+
+    private lateinit var recyclerView: RecyclerView
+    private lateinit var transactionAdapter: TransactionAdapter
+    private val transactionList = mutableListOf<Transaction>()
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+        val root = inflater.inflate(R.layout.fragment_transactions, container, false)
+
+        recyclerView = root.findViewById(R.id.recyclerViewTransactions)
+        recyclerView.layoutManager = LinearLayoutManager(requireContext())
+        transactionAdapter = TransactionAdapter(transactionList)
+        recyclerView.adapter = transactionAdapter
+
+        return root
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        // Simulate loading transactions from a data source
+        loadTransactions()
+    }
+
+    private fun loadTransactions() {
+        transactionViewModel.allTransactions.observe(viewLifecycleOwner, { transactions ->
+            transactionList.clear()
+            transactionList.addAll(transactions.map { Transaction(it.title, it.nominal) })
+            transactionAdapter.notifyDataSetChanged()
+        })
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_transaction_list.xml b/app/src/main/res/layout/activity_transaction_list.xml
new file mode 100644
index 0000000..58a86db
--- /dev/null
+++ b/app/src/main/res/layout/activity_transaction_list.xml
@@ -0,0 +1,11 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/recyclerViewTransactions"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_transaction.xml b/app/src/main/res/layout/item_transaction.xml
new file mode 100644
index 0000000..324f70a
--- /dev/null
+++ b/app/src/main/res/layout/item_transaction.xml
@@ -0,0 +1,38 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:padding="16dp">
+
+    <TextView
+        android:id="@+id/textViewDate"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="16sp"
+        android:textStyle="bold" />
+
+    <TextView
+        android:id="@+id/textViewTitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="16sp" />
+
+    <TextView
+        android:id="@+id/textViewCategory"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="14sp" />
+
+    <TextView
+        android:id="@+id/textViewPrice"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="14sp" />
+
+    <TextView
+        android:id="@+id/textViewLocation"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="14sp" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml
index 9439eda..f34e15b 100644
--- a/app/src/main/res/navigation/mobile_navigation.xml
+++ b/app/src/main/res/navigation/mobile_navigation.xml
@@ -22,4 +22,11 @@
         android:name="com.example.myapplication.ui.notifications.NotificationsFragment"
         android:label="@string/title_notifications"
         tools:layout="@layout/fragment_notifications" />
+
+        <fragment
+        android:id="@+id/navigation_transactions"
+        android:name="com.example.myapplication.ui.transactions.TransactionsFragment"
+        android:label="@string/title_transactions"
+        tools:layout="@layout/fragment_transactions" />
+        
 </navigation>
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 7c90f5c..0991321 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Sat Mar 16 13:51:19 WIB 2024
+#Thu Mar 21 16:32:17 WIB 2024
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-- 
GitLab