Merge branch 'master' into screen-features

# Conflicts:
#	app/src/main/AndroidManifest.xml
#	app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt
This commit is contained in:
shaulascr
2025-04-20 22:43:25 +07:00
27 changed files with 1531 additions and 197 deletions

View File

@ -18,7 +18,7 @@ val localProperties = Properties().apply {
android { android {
namespace = "com.alya.ecommerce_serang" namespace = "com.alya.ecommerce_serang"
compileSdk = 34 compileSdk = 35
defaultConfig { defaultConfig {
applicationId = "com.alya.ecommerce_serang" applicationId = "com.alya.ecommerce_serang"
@ -38,14 +38,14 @@ android {
buildTypes { buildTypes {
release { release {
buildConfigField("String",
"BASE_URL",
"\"${localProperties["BASE_URL"] ?: "http://default-url.com/"}\"")
isMinifyEnabled = false isMinifyEnabled = false
proguardFiles( proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"), getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro" "proguard-rules.pro"
) )
buildConfigField("String",
"BASE_URL",
"\"${localProperties["BASE_URL"] ?: "http://default-url.com/"}\"")
} }
debug { debug {
buildConfigField("String", buildConfigField("String",
@ -79,6 +79,7 @@ dependencies {
implementation(libs.androidx.fragment.ktx) implementation(libs.androidx.fragment.ktx)
implementation(libs.androidx.navigation.fragment.ktx) implementation(libs.androidx.navigation.fragment.ktx)
implementation(libs.androidx.navigation.ui.ktx) implementation(libs.androidx.navigation.ui.ktx)
implementation(libs.androidx.recyclerview)
testImplementation(libs.junit) testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core) androidTestImplementation(libs.androidx.espresso.core)
@ -94,7 +95,6 @@ dependencies {
implementation("de.hdodenhof:circleimageview:3.1.0") implementation("de.hdodenhof:circleimageview:3.1.0")
// implementation(libs.hilt.android) // implementation(libs.hilt.android)
// kapt("com.google.dagger:hilt-compiler:2.48") // kapt("com.google.dagger:hilt-compiler:2.48")
// //
@ -103,3 +103,4 @@ dependencies {
// kapt("androidx.hilt:hilt-compiler:1.0.0") // kapt("androidx.hilt:hilt-compiler:1.0.0")
} }

View File

@ -6,6 +6,8 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32"/>

View File

@ -29,7 +29,7 @@ data class Product(
val categoryId: Int? = null, val categoryId: Int? = null,
@field:SerializedName("price") @field:SerializedName("price")
val price: String? = null, val price: Int? = null,
@field:SerializedName("name") @field:SerializedName("name")
val name: String? = null, val name: String? = null,

View File

@ -1,6 +1,5 @@
package com.alya.ecommerce_serang.data.api.response package com.alya.ecommerce_serang.data.api.response.product
import com.alya.ecommerce_serang.data.api.response.product.Product
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
data class CreateProductResponse( data class CreateProductResponse(

View File

@ -7,6 +7,7 @@ import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
class ApiConfig { class ApiConfig {
companion object { companion object {

View File

@ -11,7 +11,10 @@ import com.alya.ecommerce_serang.data.api.dto.OrderRequestBuy
import com.alya.ecommerce_serang.data.api.dto.OtpRequest import com.alya.ecommerce_serang.data.api.dto.OtpRequest
import com.alya.ecommerce_serang.data.api.dto.RegisterRequest import com.alya.ecommerce_serang.data.api.dto.RegisterRequest
import com.alya.ecommerce_serang.data.api.dto.UpdateCart import com.alya.ecommerce_serang.data.api.dto.UpdateCart
import com.alya.ecommerce_serang.data.api.response.product.CreateProductResponse
import com.alya.ecommerce_serang.data.api.response.ViewStoreProductsResponse import com.alya.ecommerce_serang.data.api.response.ViewStoreProductsResponse
import okhttp3.MultipartBody
import okhttp3.RequestBody
import com.alya.ecommerce_serang.data.api.response.auth.LoginResponse import com.alya.ecommerce_serang.data.api.response.auth.LoginResponse
import com.alya.ecommerce_serang.data.api.response.auth.OtpResponse import com.alya.ecommerce_serang.data.api.response.auth.OtpResponse
import com.alya.ecommerce_serang.data.api.response.auth.RegisterResponse import com.alya.ecommerce_serang.data.api.response.auth.RegisterResponse
@ -36,14 +39,14 @@ import com.alya.ecommerce_serang.data.api.response.product.StoreResponse
import com.alya.ecommerce_serang.data.api.response.profile.AddressResponse import com.alya.ecommerce_serang.data.api.response.profile.AddressResponse
import com.alya.ecommerce_serang.data.api.response.profile.CreateAddressResponse import com.alya.ecommerce_serang.data.api.response.profile.CreateAddressResponse
import com.alya.ecommerce_serang.data.api.response.profile.ProfileResponse import com.alya.ecommerce_serang.data.api.response.profile.ProfileResponse
import okhttp3.MultipartBody
import okhttp3.RequestBody
import retrofit2.Call import retrofit2.Call
import retrofit2.Response import retrofit2.Response
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.Field import retrofit2.http.Field
import retrofit2.http.FormUrlEncoded import retrofit2.http.FormUrlEncoded
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.HeaderMap
import retrofit2.http.Multipart import retrofit2.http.Multipart
import retrofit2.http.POST import retrofit2.http.POST
import retrofit2.http.PUT import retrofit2.http.PUT
@ -142,20 +145,23 @@ interface ApiService {
@GET("category") @GET("category")
fun getCategories(): Call<CategoryResponse> fun getCategories(): Call<CategoryResponse>
@Multipart
@POST("store/createproduct") @POST("store/createproduct")
@FormUrlEncoded
suspend fun addProduct( suspend fun addProduct(
@Field("name") name: String, @Part("name") name: RequestBody,
@Field("description") description: String, @Part("description") description: RequestBody,
@Field("price") price: Int, @Part("price") price: RequestBody,
@Field("stock") stock: Int, @Part("stock") stock: RequestBody,
@Field("min_order") minOrder: Int, @Part("min_order") minOrder: RequestBody,
@Field("weight") weight: Int, @Part("weight") weight: RequestBody,
@Field("is_pre_order") isPreOrder: Boolean, @Part("is_pre_order") isPreOrder: RequestBody,
@Field("duration") duration: Int, @Part("duration") duration: RequestBody,
@Field("category_id") categoryId: Int, @Part("category_id") categoryId: RequestBody,
@Field("is_active") isActive: String @Part("status") status: RequestBody,
): Response<Unit> @Part image: MultipartBody.Part?,
@Part sppirt: MultipartBody.Part?,
@Part halal: MultipartBody.Part?
): Response<CreateProductResponse>
@GET("cart_item") @GET("cart_item")
suspend fun getCart (): Response<ListCartResponse> suspend fun getCart (): Response<ListCartResponse>

View File

@ -4,13 +4,19 @@ import android.util.Log
import com.alya.ecommerce_serang.data.api.dto.CartItem import com.alya.ecommerce_serang.data.api.dto.CartItem
import com.alya.ecommerce_serang.data.api.dto.CategoryItem import com.alya.ecommerce_serang.data.api.dto.CategoryItem
import com.alya.ecommerce_serang.data.api.dto.ProductsItem import com.alya.ecommerce_serang.data.api.dto.ProductsItem
import com.alya.ecommerce_serang.data.api.response.product.CreateProductResponse
import com.alya.ecommerce_serang.data.api.response.cart.AddCartResponse import com.alya.ecommerce_serang.data.api.response.cart.AddCartResponse
import com.alya.ecommerce_serang.data.api.response.product.ProductResponse import com.alya.ecommerce_serang.data.api.response.product.ProductResponse
import com.alya.ecommerce_serang.data.api.response.product.ReviewsItem import com.alya.ecommerce_serang.data.api.response.product.ReviewsItem
import com.alya.ecommerce_serang.data.api.response.product.StoreProduct import com.alya.ecommerce_serang.data.api.response.product.StoreProduct
import com.alya.ecommerce_serang.data.api.retrofit.ApiService import com.alya.ecommerce_serang.data.api.retrofit.ApiService
import com.alya.ecommerce_serang.utils.SessionManager
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody
import java.io.File
class ProductRepository(private val apiService: ApiService) { class ProductRepository(private val apiService: ApiService) {
suspend fun getAllProducts(): Result<List<ProductsItem>> = suspend fun getAllProducts(): Result<List<ProductsItem>> =
@ -131,12 +137,16 @@ class ProductRepository(private val apiService: ApiService) {
} }
suspend fun fetchMyStoreProducts(): List<ProductsItem> { suspend fun fetchMyStoreProducts(): List<ProductsItem> {
val response = apiService.getStoreProduct() return try {
if (response.isSuccessful) { val response = apiService.getStoreProduct()
val responseBody = response.body() if (response.isSuccessful) {
return responseBody?.products?.filterNotNull() ?: emptyList() response.body()?.products?.filterNotNull() ?: emptyList()
} else { } else {
throw Exception("Failed to fetch store products: ${response.message()}") throw Exception("Failed to fetch store products: ${response.message()}")
}
} catch (e: Exception) {
Log.e("ProductRepository", "Error fetching store products", e)
throw e
} }
} }
@ -150,33 +160,39 @@ class ProductRepository(private val apiService: ApiService) {
isPreOrder: Boolean, isPreOrder: Boolean,
duration: Int, duration: Int,
categoryId: Int, categoryId: Int,
isActive: Boolean status: String,
): Result<Unit> = withContext(Dispatchers.IO) { imagePart: MultipartBody.Part?,
try { sppirtPart: MultipartBody.Part?,
val status = if (isActive) "active" else "inactive" halalPart: MultipartBody.Part?
): Result<CreateProductResponse> {
return try {
val response = apiService.addProduct( val response = apiService.addProduct(
name = name, name = RequestBody.create("text/plain".toMediaTypeOrNull(), name),
description = description, description = RequestBody.create("text/plain".toMediaTypeOrNull(), description),
price = price, price = RequestBody.create("text/plain".toMediaTypeOrNull(), price.toString()),
stock = stock, stock = RequestBody.create("text/plain".toMediaTypeOrNull(), stock.toString()),
minOrder = minOrder, minOrder = RequestBody.create("text/plain".toMediaTypeOrNull(), minOrder.toString()),
weight = weight, weight = RequestBody.create("text/plain".toMediaTypeOrNull(), weight.toString()),
isPreOrder = isPreOrder, isPreOrder = RequestBody.create("text/plain".toMediaTypeOrNull(), isPreOrder.toString()),
duration = duration, duration = RequestBody.create("text/plain".toMediaTypeOrNull(), duration.toString()),
categoryId = categoryId, categoryId = RequestBody.create("text/plain".toMediaTypeOrNull(), categoryId.toString()),
isActive = status status = RequestBody.create("text/plain".toMediaTypeOrNull(), status),
image = imagePart,
sppirt = sppirtPart,
halal = halalPart
) )
if (response.isSuccessful) { if (response.isSuccessful) {
Result.Success(Unit) Result.Success(response.body()!!)
} else { } else {
Result.Error(Exception("Failed to add product. Code: ${response.code()}")) Result.Error(Exception("Failed to create product: ${response.code()}"))
} }
} catch (e: Exception) { } catch (e: Exception) {
Result.Error(e) Result.Error(e)
} }
} }
companion object { companion object {
private const val TAG = "ProductRepository" private const val TAG = "ProductRepository"
} }

View File

@ -4,10 +4,14 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import android.widget.PopupMenu
import android.widget.TextView import android.widget.TextView
import android.widget.Toast
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.alya.ecommerce_serang.R import com.alya.ecommerce_serang.R
import com.alya.ecommerce_serang.data.api.dto.Product
import com.alya.ecommerce_serang.data.api.dto.ProductsItem import com.alya.ecommerce_serang.data.api.dto.ProductsItem
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
@ -22,27 +26,37 @@ class ProductAdapter(
private val tvProductPrice: TextView = itemView.findViewById(R.id.tv_product_price) private val tvProductPrice: TextView = itemView.findViewById(R.id.tv_product_price)
private val tvProductStock: TextView = itemView.findViewById(R.id.tv_product_stock) private val tvProductStock: TextView = itemView.findViewById(R.id.tv_product_stock)
private val tvProductStatus: TextView = itemView.findViewById(R.id.tv_product_status) private val tvProductStatus: TextView = itemView.findViewById(R.id.tv_product_status)
private val ivMenu: ImageView = itemView.findViewById(R.id.iv_menu)
fun bind(product: ProductsItem) { fun bind(product: ProductsItem) {
tvProductName.text = product.name tvProductName.text = product.name
tvProductPrice.text = "Rp${product.price}" tvProductPrice.text = "Rp${product.price}"
tvProductStock.text = "Stok: ${product.stock}" tvProductStock.text = "Stok: ${product.stock}"
tvProductStatus.text = product.status
// Change color depending on status if (product.status.equals("active",true)) {
tvProductStatus.setTextColor( tvProductStatus.text = "Aktif"
ContextCompat.getColor( tvProductStatus.setTextColor(ContextCompat.getColor(itemView.context, R.color.darkblue_500))
itemView.context, tvProductStatus.background = ContextCompat.getDrawable(itemView.context, R.drawable.bg_product_active)
if (product.status.equals("active", true)) } else {
R.color.darkblue_500 else R.color.black_500 tvProductStatus.text = "Nonaktif"
) tvProductStatus.setTextColor(ContextCompat.getColor(itemView.context, R.color.black_500))
) tvProductStatus.background = ContextCompat.getDrawable(itemView.context, R.drawable.bg_product_inactive)
}
Glide.with(itemView.context) Glide.with(itemView.context)
.load(product.image) .load(product.image)
.placeholder(R.drawable.placeholder_image) .placeholder(R.drawable.placeholder_image)
.into(ivProduct) .into(ivProduct)
ivMenu.setOnClickListener {
// Show Bottom Sheet when menu is clicked
val bottomSheetFragment = ProductOptionsBottomSheetFragment(product)
bottomSheetFragment.show(
(itemView.context as FragmentActivity).supportFragmentManager,
bottomSheetFragment.tag
)
}
itemView.setOnClickListener { itemView.setOnClickListener {
onItemClick(product) onItemClick(product)
} }

View File

@ -0,0 +1,46 @@
package com.alya.ecommerce_serang.ui.profile.mystore.product
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.alya.ecommerce_serang.R
import com.alya.ecommerce_serang.data.api.dto.ProductsItem
import com.alya.ecommerce_serang.databinding.FragmentProductOptionsBottomSheetBinding
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
class ProductOptionsBottomSheetFragment(private val product: ProductsItem) : BottomSheetDialogFragment() {
private var _binding: FragmentProductOptionsBottomSheetBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentProductOptionsBottomSheetBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.btnEditProduct.setOnClickListener {
// Handle editing product
// Example: Open the edit activity or fragment
dismiss()
}
binding.btnDeleteProduct.setOnClickListener {
// Handle deleting product
// Example: Show confirmation dialog
dismiss()
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}

View File

@ -1,11 +1,15 @@
package com.alya.ecommerce_serang.ui.profile.mystore.product package com.alya.ecommerce_serang.ui.profile.mystore.product
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.util.Log
import android.text.TextWatcher
import android.view.View import android.view.View
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.Toast import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import com.alya.ecommerce_serang.R import com.alya.ecommerce_serang.R
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
@ -18,6 +22,11 @@ import com.alya.ecommerce_serang.databinding.ActivityStoreProductDetailBinding
import com.alya.ecommerce_serang.utils.viewmodel.ProductViewModel import com.alya.ecommerce_serang.utils.viewmodel.ProductViewModel
import com.alya.ecommerce_serang.utils.BaseViewModelFactory import com.alya.ecommerce_serang.utils.BaseViewModelFactory
import com.alya.ecommerce_serang.utils.SessionManager import com.alya.ecommerce_serang.utils.SessionManager
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody
import java.io.File
import java.io.FileOutputStream
import kotlin.getValue import kotlin.getValue
class StoreProductDetailActivity : AppCompatActivity() { class StoreProductDetailActivity : AppCompatActivity() {
@ -25,6 +34,9 @@ class StoreProductDetailActivity : AppCompatActivity() {
private lateinit var binding: ActivityStoreProductDetailBinding private lateinit var binding: ActivityStoreProductDetailBinding
private lateinit var sessionManager: SessionManager private lateinit var sessionManager: SessionManager
private lateinit var categoryList: List<CategoryItem> private lateinit var categoryList: List<CategoryItem>
private var imageUri: Uri? = null
private var sppirtUri: Uri? = null
private var halalUri: Uri? = null
private val viewModel: ProductViewModel by viewModels { private val viewModel: ProductViewModel by viewModels {
BaseViewModelFactory { BaseViewModelFactory {
@ -35,14 +47,50 @@ class StoreProductDetailActivity : AppCompatActivity() {
} }
} }
private val imagePickerLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
imageUri = result.data?.data
imageUri?.let {
binding.ivPreviewFoto.setImageURI(it)
binding.switcherFotoProduk.showNext()
}
validateForm()
}
}
private val sppirtLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
if (uri != null && isValidFile(uri)) {
sppirtUri = uri
binding.tvSppirtName.text = getFileName(uri)
binding.switcherSppirt.showNext()
}
}
private val halalLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
if (uri != null && isValidFile(uri)) {
halalUri = uri
binding.tvHalalName.text = getFileName(uri)
binding.switcherHalal.showNext()
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityStoreProductDetailBinding.inflate(layoutInflater) binding = ActivityStoreProductDetailBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
setupHeader() setupHeader()
observeCategories()
// Fetch categories
viewModel.loadCategories() viewModel.loadCategories()
viewModel.categoryList.observe(this) { result ->
if (result is Result.Success) {
categoryList = result.data
val adapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, categoryList.map { it.name })
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
binding.spinnerKategoriProduk.adapter = adapter
}
}
// Setup Pre-Order visibility // Setup Pre-Order visibility
binding.switchIsPreOrder.setOnCheckedChangeListener { _, isChecked -> binding.switchIsPreOrder.setOnCheckedChangeListener { _, isChecked ->
@ -50,126 +98,147 @@ class StoreProductDetailActivity : AppCompatActivity() {
validateForm() validateForm()
} }
setupFormValidation() binding.tvTambahFoto.setOnClickListener {
val intent = Intent(Intent.ACTION_PICK).apply { type = "image/*" }
imagePickerLauncher.launch(intent)
}
binding.layoutUploadFoto.setOnClickListener {
val intent = Intent(Intent.ACTION_PICK).apply { type = "image/*" }
imagePickerLauncher.launch(intent)
}
binding.btnRemoveFoto.setOnClickListener {
imageUri = null
binding.switcherFotoProduk.showPrevious()
validateForm()
}
binding.layoutUploadSppirt.setOnClickListener { sppirtLauncher.launch("*/*") }
binding.btnRemoveSppirt.setOnClickListener {
sppirtUri = null
binding.switcherSppirt.showPrevious()
}
binding.layoutUploadHalal.setOnClickListener { halalLauncher.launch("*/*") }
binding.btnRemoveHalal.setOnClickListener {
halalUri = null
binding.switcherHalal.showPrevious()
}
validateForm() validateForm()
binding.btnSaveProduct.setOnClickListener { binding.btnSaveProduct.setOnClickListener {
if (binding.btnSaveProduct.isEnabled) addProduct() if (!binding.btnSaveProduct.isEnabled) {
return@setOnClickListener
}
submitProduct()
}
}
private fun isValidFile(uri: Uri): Boolean {
val mimeType = contentResolver.getType(uri) ?: return false
return listOf("application/pdf", "image/jpeg", "image/png", "image/jpg").contains(mimeType)
}
private fun getFileName(uri: Uri): String {
return uri.lastPathSegment?.split("/")?.last() ?: "unknown_file"
}
private fun validateForm() {
val valid = binding.edtNamaProduk.text.isNotBlank() &&
binding.edtDeskripsiProduk.text.isNotBlank() &&
binding.edtHargaProduk.text.isNotBlank() &&
binding.edtStokProduk.text.isNotBlank() &&
binding.edtMinOrder.text.isNotBlank() &&
binding.edtBeratProduk.text.isNotBlank() &&
(!binding.switchIsPreOrder.isChecked || binding.edtDurasi.text.isNotBlank()) &&
imageUri != null
binding.btnSaveProduct.isEnabled = valid
binding.btnSaveProduct.setTextColor(
if (valid) ContextCompat.getColor(this, R.color.white) else ContextCompat.getColor(this, R.color.black_300)
)
binding.btnSaveProduct.setBackgroundResource(
if (valid) R.drawable.bg_button_active else R.drawable.bg_button_disabled
)
}
private fun uriToNamedFile(uri: Uri, context: Context, prefix: String): File {
val extension = context.contentResolver.getType(uri)?.substringAfter("/") ?: "jpg"
val filename = "$prefix-${System.currentTimeMillis()}.$extension"
val file = File(context.cacheDir, filename)
context.contentResolver.openInputStream(uri)?.use { input ->
FileOutputStream(file).use { output -> input.copyTo(output) }
}
return file
}
private fun submitProduct() {
val name = binding.edtNamaProduk.text.toString()
val description = binding.edtDeskripsiProduk.text.toString()
val price = binding.edtHargaProduk.text.toString().toInt()
val stock = binding.edtStokProduk.text.toString().toInt()
val minOrder = binding.edtMinOrder.text.toString().toInt()
val weight = binding.edtBeratProduk.text.toString().toInt()
val isPreOrder = binding.switchIsPreOrder.isChecked
val duration = if (isPreOrder) binding.edtDurasi.text.toString().toInt() else 0
val status = if (binding.switchIsActive.isChecked) "active" else "inactive"
val categoryId = categoryList.getOrNull(binding.spinnerKategoriProduk.selectedItemPosition)?.id ?: 0
val imageFile = imageUri?.let { uriToNamedFile(it, this, "productimg") }
val sppirtFile = sppirtUri?.let { uriToNamedFile(it, this, "sppirt") }
val halalFile = halalUri?.let { uriToNamedFile(it, this, "halal") }
Log.d("File URI", "SPPIRT URI: ${sppirtUri.toString()}")
Log.d("File URI", "Halal URI: ${halalUri.toString()}")
val imagePart = imageFile?.let { createPartFromFile("productimg", it) }
val sppirtPart = sppirtFile?.let { createPartFromFile("sppirt", it) }
val halalPart = halalFile?.let { createPartFromFile("halal", it) }
viewModel.addProduct(
name, description, price, stock, minOrder, weight, isPreOrder, duration, categoryId, status, imagePart, sppirtPart, halalPart
)
viewModel.productCreationResult.observe(this) { result ->
when (result) {
is Result.Loading -> binding.btnSaveProduct.isEnabled = false
is Result.Success -> {
val product = result.data.product
Toast.makeText(this, "Product Created: ${product?.productName}", Toast.LENGTH_SHORT).show()
finish()
}
is Result.Error -> {
Log.e("ProductDetailActivity", "Error: ${result.exception.message}")
binding.btnSaveProduct.isEnabled = true
}
}
}
}
fun getMimeType(file: File): String {
val extension = file.extension
return when (extension.lowercase()) {
"jpg", "jpeg" -> "image/jpeg"
"png" -> "image/png"
"pdf" -> "application/pdf"
else -> "application/octet-stream"
}
}
fun createPartFromFile(field: String, file: File?): MultipartBody.Part? {
return file?.let {
val mimeType = getMimeType(it).toMediaTypeOrNull()
val requestBody = RequestBody.create(mimeType, it)
MultipartBody.Part.createFormData(field, it.name, requestBody)
} }
} }
private fun setupHeader() { private fun setupHeader() {
binding.header.headerTitle.text = "Tambah Produk" binding.header.headerTitle.text = "Tambah Produk"
binding.header.headerLeftIcon.setOnClickListener { onBackPressedDispatcher.onBackPressed() }
binding.header.headerLeftIcon.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
}
private fun observeCategories() {
viewModel.categoryList.observe(this) { result ->
when (result) {
is Result.Loading -> {
// Optionally show loading spinner
}
is Result.Success -> {
categoryList = result.data
setupCategorySpinner(categoryList)
}
is Result.Error -> {
Toast.makeText(
this,
"Failed to load categories: ${result.exception.message}",
Toast.LENGTH_SHORT
).show()
}
}
}
}
private fun setupCategorySpinner(categories: List<CategoryItem>) {
val categoryNames = categories.map { it.name }
val adapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, categoryNames)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
binding.spinnerKategoriProduk.adapter = adapter
}
private fun addProduct() {
val name = binding.edtNamaProduk.text.toString()
val description = binding.edtDeskripsiProduk.text.toString()
val price = binding.edtHargaProduk.text.toString().toIntOrNull() ?: 0
val stock = binding.edtStokProduk.text.toString().toIntOrNull() ?: 0
val minOrder = binding.edtMinOrder.text.toString().toIntOrNull() ?: 1
val weight = binding.edtBeratProduk.text.toString().toIntOrNull() ?: 0
val isPreOrder = binding.switchIsPreOrder.isChecked
val duration = binding.edtDurasi.text.toString().toIntOrNull() ?: 0
val isActive = binding.switchIsActive.isChecked
val categoryPosition = binding.spinnerKategoriProduk.selectedItemPosition
val categoryId = categoryList.getOrNull(categoryPosition)?.id ?: 0
if (isPreOrder && duration == 0) {
Toast.makeText(this, "Durasi wajib diisi jika pre-order diaktifkan.", Toast.LENGTH_SHORT).show()
return
}
viewModel.addProduct(
name, description, price, stock, minOrder, weight, isPreOrder, duration, categoryId, isActive
).observe(this) { result ->
when (result) {
is Result.Loading -> binding.btnSaveProduct.isEnabled = false
is Result.Success -> {
Toast.makeText(this, "Produk berhasil ditambahkan!", Toast.LENGTH_SHORT).show()
finish()
}
is Result.Error -> {
binding.btnSaveProduct.isEnabled = true
Toast.makeText(this, "Gagal: ${result.exception.message}", Toast.LENGTH_SHORT).show()
}
}
}
}
private fun setupFormValidation() {
val watcher = object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
override fun afterTextChanged(s: Editable?) {
validateForm()
}
}
// Watch all fields
binding.edtNamaProduk.addTextChangedListener(watcher)
binding.edtDeskripsiProduk.addTextChangedListener(watcher)
binding.edtHargaProduk.addTextChangedListener(watcher)
binding.edtStokProduk.addTextChangedListener(watcher)
binding.edtMinOrder.addTextChangedListener(watcher)
binding.edtBeratProduk.addTextChangedListener(watcher)
binding.edtDurasi.addTextChangedListener(watcher)
}
private fun validateForm() {
val isNameValid = binding.edtNamaProduk.text.toString().isNotBlank()
val isDescriptionValid = binding.edtDeskripsiProduk.text.toString().isNotBlank()
val isPriceValid = binding.edtHargaProduk.text.toString().isNotBlank()
val isStockValid = binding.edtStokProduk.text.toString().isNotBlank()
val isMinOrderValid = binding.edtMinOrder.text.toString().isNotBlank()
val isWeightValid = binding.edtBeratProduk.text.toString().isNotBlank()
val isPreOrderChecked = binding.switchIsPreOrder.isChecked
val isDurationValid = !isPreOrderChecked || binding.edtDurasi.text.toString().isNotBlank()
val isFormValid = isNameValid && isDescriptionValid && isPriceValid &&
isStockValid && isMinOrderValid && isWeightValid && isDurationValid
if (isFormValid) {
binding.btnSaveProduct.isEnabled = true
binding.btnSaveProduct.setBackgroundResource(R.drawable.bg_button_active)
binding.btnSaveProduct.setTextColor(ContextCompat.getColor(this, R.color.white))
} else {
binding.btnSaveProduct.isEnabled = false
binding.btnSaveProduct.setBackgroundResource(R.drawable.bg_button_disabled)
binding.btnSaveProduct.setTextColor(ContextCompat.getColor(this, R.color.black_300))
}
} }
} }

View File

@ -3,19 +3,23 @@ package com.alya.ecommerce_serang.utils.viewmodel
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.liveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.alya.ecommerce_serang.data.api.dto.CategoryItem import com.alya.ecommerce_serang.data.api.dto.CategoryItem
import com.alya.ecommerce_serang.data.api.dto.ProductsItem import com.alya.ecommerce_serang.data.api.dto.ProductsItem
import com.alya.ecommerce_serang.data.api.response.product.CreateProductResponse
import com.alya.ecommerce_serang.data.api.response.product.Product import com.alya.ecommerce_serang.data.api.response.product.Product
import com.alya.ecommerce_serang.data.api.response.product.ReviewsItem import com.alya.ecommerce_serang.data.api.response.product.ReviewsItem
import com.alya.ecommerce_serang.data.api.response.product.StoreProduct import com.alya.ecommerce_serang.data.api.response.product.StoreProduct
import com.alya.ecommerce_serang.data.repository.ProductRepository import com.alya.ecommerce_serang.data.repository.ProductRepository
import com.alya.ecommerce_serang.data.repository.Result import com.alya.ecommerce_serang.data.repository.Result
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import okhttp3.MultipartBody
class ProductViewModel(private val repository: ProductRepository) : ViewModel() { class ProductViewModel(private val repository: ProductRepository) : ViewModel() {
private val _productCreationResult = MutableLiveData<Result<CreateProductResponse>>()
val productCreationResult: LiveData<Result<CreateProductResponse>> get() = _productCreationResult
private val _productDetail = MutableLiveData<Product?>() private val _productDetail = MutableLiveData<Product?>()
val productDetail: LiveData<Product?> get() = _productDetail val productDetail: LiveData<Product?> get() = _productDetail
@ -74,17 +78,20 @@ class ProductViewModel(private val repository: ProductRepository) : ViewModel()
isPreOrder: Boolean, isPreOrder: Boolean,
duration: Int, duration: Int,
categoryId: Int, categoryId: Int,
isActive: Boolean status: String,
): LiveData<Result<Unit>> = liveData { imagePart: MultipartBody.Part?,
emit(Result.Loading) sppirtPart: MultipartBody.Part?,
val result = repository.addProduct( halalPart: MultipartBody.Part?
name, description, price, stock, minOrder, weight, isPreOrder, duration, categoryId, isActive ) {
) _productCreationResult.value = Result.Loading
emit(result) viewModelScope.launch {
val result = repository.addProduct(
name, description, price, stock, minOrder, weight, isPreOrder, duration, categoryId, status, imagePart, sppirtPart, halalPart
)
_productCreationResult.value = result
}
} }
// Optional: for store detail if you need it later // Optional: for store detail if you need it later
// fun loadStoreDetail(storeId: Int) { // fun loadStoreDetail(storeId: Int) {
// viewModelScope.launch { // viewModelScope.launch {
@ -92,4 +99,5 @@ class ProductViewModel(private val repository: ProductRepository) : ViewModel()
// _storeDetail.value = storeResult // _storeDetail.value = storeResult
// } // }
// } // }
} }

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:width="1dp"
android:color="@color/black_100"
android:dashWidth="5dp"
android:dashGap="5dp" />
<solid android:color="@color/white" />
</shape>

Binary file not shown.

After

Width:  |  Height:  |  Size: 521 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/darkblue_500" />
<corners
android:topRightRadius="4dp"
android:bottomRightRadius="4dp"/>
</shape>

View File

@ -75,7 +75,6 @@
<Button <Button
android:id="@+id/btn_edit_profile" android:id="@+id/btn_edit_profile"
android:text="Ubah Profil Toko" android:text="Ubah Profil Toko"
android:textColor="@color/blue_500"
style="@style/button.small.secondary.short" style="@style/button.small.secondary.short"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
@ -83,7 +82,7 @@
<View <View
android:id="@+id/line_profile" android:id="@+id/line_profile"
android:layout_width="380dp" android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="1dp"
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:background="@color/black_50" android:background="@color/black_50"

View File

@ -69,15 +69,61 @@
</LinearLayout> </LinearLayout>
<!-- ImageView untuk preview gambar --> <!-- ViewSwitcher untuk Upload Gambar Produk -->
<ImageView <ViewSwitcher
android:id="@+id/iv_preview_foto" android:id="@+id/switcher_foto_produk"
android:layout_width="72dp" android:layout_width="wrap_content"
android:layout_height="72dp" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="10dp"
android:scaleType="centerCrop" android:layout_marginBottom="24dp">
android:contentDescription="Preview Gambar"
android:src="@drawable/ic_upload"/> <!-- Tampilan saat belum ada gambar di-upload -->
<FrameLayout
android:id="@+id/layout_upload_foto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_upload"
android:gravity="center"
android:padding="13dp">
<ImageView
android:layout_width="44dp"
android:layout_height="44dp"
android:src="@drawable/ic_upload"
android:contentDescription="Ikon Upload" />
</FrameLayout>
<!-- Tampilan saat gambar sudah di-upload -->
<FrameLayout
android:layout_width="70dp"
android:layout_height="70dp"
android:background="@drawable/bg_upload"
android:layout_gravity="center">
<ImageView
android:id="@+id/iv_preview_foto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:contentDescription="Preview Gambar"
android:src="@drawable/placeholder_image"/>
<ImageButton
android:id="@+id/btn_remove_foto"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_gravity="top|end"
android:layout_margin="6dp"
android:elevation="8dp"
android:src="@drawable/ic_close"
android:contentDescription="Hapus Gambar"
android:padding="4dp"
app:tint="@color/white" />
</FrameLayout>
</ViewSwitcher>
</LinearLayout> </LinearLayout>
@ -245,7 +291,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Harga Produk" android:text="Harga Produk"
style="@style/body_medium" style="@style/body_medium"
android:layout_marginRight="4dp"/> android:layout_marginEnd="4dp"/>
<TextView <TextView
android:layout_width="0dp" android:layout_width="0dp"
@ -409,7 +455,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:background="@drawable/bg_text_field" android:background="@drawable/bg_text_field"
android:gravity="center"> android:gravity="center"
android:layout_marginTop="10dp">
<EditText <EditText
android:id="@+id/edt_berat_produk" android:id="@+id/edt_berat_produk"
@ -549,7 +596,6 @@
style="@style/body_medium" style="@style/body_medium"
android:layout_marginEnd="4dp"/> android:layout_marginEnd="4dp"/>
<TextView <TextView
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -567,7 +613,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:background="@drawable/bg_text_field" android:background="@drawable/bg_text_field"
android:gravity="center"> android:gravity="center"
android:layout_marginTop="10dp">
<EditText <EditText
android:id="@+id/edt_durasi" android:id="@+id/edt_durasi"
@ -592,6 +639,178 @@
</LinearLayout> </LinearLayout>
<!-- SPPIRT -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginBottom="24dp">
<!-- Label SPPIRT -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="SPPIRT"
style="@style/body_medium"/>
<!-- ViewSwitcher: Switches antara upload box dan uploaded preview SPPIRT -->
<ViewSwitcher
android:id="@+id/switcher_sppirt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp">
<!-- Upload Prompt Layout -->
<FrameLayout
android:id="@+id/layout_upload_sppirt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_upload"
android:padding="20dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center"
android:gravity="center">
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/ic_upload"
android:contentDescription="Ikon Unggah" />
<TextView
style="@style/body_small"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Unggah dokumen SPPIRT Anda di sini"
android:textColor="@color/black_300"
android:layout_marginTop="8dp"/>
</LinearLayout>
</FrameLayout>
<!-- Uploaded File Preview Layout -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="@drawable/bg_text_field"
android:gravity="center"
android:layout_marginTop="10dp"
android:padding="8dp">
<TextView
android:id="@+id/tv_sppirt_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="sppirt.pdf"
style="@style/body_small"/>
<ImageButton
android:id="@+id/btn_remove_sppirt"
android:layout_width="12dp"
android:layout_height="12dp"
android:src="@drawable/ic_close"
app:tint="@color/black_300"
android:contentDescription="Hapus unggahan" />
</LinearLayout>
</ViewSwitcher>
</LinearLayout>
<!-- Halal -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginBottom="24dp">
<!-- Label Halal -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sertifikasi Halal"
style="@style/body_medium"/>
<!-- ViewSwitcher: Switches antara upload box dan uploaded preview halal -->
<ViewSwitcher
android:id="@+id/switcher_halal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp">
<!-- Upload Prompt Layout -->
<FrameLayout
android:id="@+id/layout_upload_halal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_upload"
android:padding="20dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center"
android:gravity="center">
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/ic_upload"
android:contentDescription="Ikon Unggah" />
<TextView
style="@style/body_small"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Unggah dokumen Sertifikasi Halal Anda di sini"
android:textColor="@color/black_300"
android:layout_marginTop="8dp"/>
</LinearLayout>
</FrameLayout>
<!-- Uploaded File Preview Layout -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="@drawable/bg_text_field"
android:gravity="center"
android:layout_marginTop="10dp"
android:padding="8dp">
<TextView
android:id="@+id/tv_halal_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="sppirt.pdf"
style="@style/body_small"/>
<ImageButton
android:id="@+id/btn_remove_halal"
android:layout_width="12dp"
android:layout_height="12dp"
android:src="@drawable/ic_close"
app:tint="@color/black_300"
android:contentDescription="Hapus unggahan" />
</LinearLayout>
</ViewSwitcher>
</LinearLayout>
<!-- Produk Aktif --> <!-- Produk Aktif -->
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -0,0 +1,55 @@
<?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="wrap_content"
android:orientation="vertical"
android:padding="16dp"
android:background="?android:attr/windowBackground">
<LinearLayout
android:id="@+id/btn_edit_product"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="12dp"
android:gravity="center_vertical">
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@drawable/ic_edit"
android:contentDescription="Edit Icon" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Ubah Produk"
style="@style/label_large"
android:layout_marginStart="16dp"/>
</LinearLayout>
<LinearLayout
android:id="@+id/btn_delete_product"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10dp"
android:gravity="center_vertical">
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@drawable/ic_delete"
android:contentDescription="Delete Icon" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hapus Produk"
style="@style/label_large"
android:layout_marginStart="16dp"/>
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,292 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:paddingTop="16dp">
<!-- Order Header -->
<View
android:id="@+id/shape_sells_title"
android:layout_width="4dp"
android:layout_height="48dp"
android:background="@drawable/shape_sells_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<LinearLayout
android:id="@+id/layout_order_header"
android:layout_width="220dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
app:layout_constraintStart_toStartOf="@id/shape_sells_title"
app:layout_constraintTop_toTopOf="parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_order_title"
style="@style/label_medium_prominent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:text="Pesanan Perlu Dibuat Tagihan"/>
<TextView
android:id="@+id/tv_order_number"
style="@style/label_small"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:textColor="@color/black_300"
android:text="No. Pesanan: 123456789"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:layout_width="16dp"
android:layout_height="16dp"
android:src="@drawable/ic_person"/>
<TextView
android:id="@+id/tv_order_customer"
style="@style/label_small"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:layout_marginStart="4dp"
android:textColor="@color/black_300"
android:text="Gracia Hotmauli"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:orientation="vertical"
android:layout_marginEnd="16dp"
android:gravity="end">
<TextView
android:layout_width="150dp"
android:layout_height="wrap_content"
android:maxLines="1"
style="@style/label_small"
android:textAlignment="textEnd"
android:text="Buat tagihan sebelum:"
android:textColor="@color/black_300" />
<TextView
android:id="@+id/tv_order_due"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="25 Okt; 23.59"
style="@style/label_small"
android:paddingHorizontal="4dp"
android:textColor="@color/darkblue_500"
android:background="@drawable/bg_product_active" />
</LinearLayout>
<!-- Order Detail -->
<LinearLayout
android:id="@+id/layout_order_detail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/layout_order_header"
android:paddingHorizontal="16dp"
android:layout_marginTop="8dp"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/black_50"/>
<!-- Product Detail -->
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layout_order_product_detail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingVertical="12dp">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/iv_order_product"
android:layout_width="95dp"
android:layout_height="64dp"
android:src="@drawable/placeholder_image"
android:scaleType="centerCrop"
android:contentDescription="Order Product Image"
app:shapeAppearanceOverlay="@style/store_product_image"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginStart="13dp"
app:layout_constraintStart_toEndOf="@id/iv_order_product"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/tv_order_product_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:text="Jaket Pink Fuschia"
style="@style/label_medium_prominent"/>
<TextView
android:id="@+id/tv_order_product_variant"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:text="S"
style="@style/label_medium"
android:textColor="@color/black_300"/>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginStart="13dp"
android:gravity="end"
app:layout_constraintStart_toEndOf="@id/iv_order_product"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@id/iv_order_product">
<TextView
android:id="@+id/tv_order_product_qty"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:text="x2"
style="@style/label_medium"
android:textColor="@color/black_300"
android:textAlignment="textEnd"/>
<TextView
android:id="@+id/tv_order_product_price"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:text="Rp150.000"
style="@style/label_medium"
android:textAlignment="textEnd"/>
</LinearLayout>
<TextView
android:id="@+id/tv_see_more"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="true"
android:text="Lihat 3 produk lainnya"
android:gravity="center"
style="@style/label_small"
android:fontFamily="@font/dmsans_italic"
android:textColor="@color/black_300"
app:layout_constraintTop_toBottomOf="@id/iv_order_product"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginVertical="8dp"
android:clickable="true"
android:visibility="gone"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/black_50"/>
<!-- Total Price -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingVertical="8dp">
<TextView
android:id="@+id/tv_order_qty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="2 produk"
style="@style/label_large"
android:layout_alignParentStart="true"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_alignParentEnd="true">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Total:"
style="@style/label_large_prominent"
android:textAlignment="textEnd"/>
<TextView
android:id="@+id/tv_order_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Rp300.000"
style="@style/label_large_prominent"
android:textColor="@color/blue_500"
android:layout_marginStart="5dp"
android:textAlignment="textEnd"/>
</LinearLayout>
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/black_50"/>
<!-- Action Buttons -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="13dp"
android:orientation="horizontal">
<Button
android:id="@+id/btn_edit_order"
style="@style/button.small.secondary.medium"
android:text="Ubah Tagihan"
android:layout_marginEnd="10dp"/>
<Button
android:id="@+id/btn_confirm_order"
style="@style/button.small.active.medium"
android:text="Konfirmasi Tagihan" />
</LinearLayout>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="8dp"
android:background="@color/black_50"
android:layout_marginTop="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/layout_order_detail"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,318 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:paddingTop="16dp">
<!-- Payment Header -->
<View
android:id="@+id/shape_sells_title"
android:layout_width="4dp"
android:layout_height="32dp"
android:background="@drawable/shape_sells_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<LinearLayout
android:id="@+id/layout_payment_header"
android:layout_width="220dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
app:layout_constraintStart_toStartOf="@id/shape_sells_title"
app:layout_constraintTop_toTopOf="parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_payment_title"
style="@style/label_medium_prominent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:text="Pesanan Telah Dibayar"/>
<TextView
android:id="@+id/tv_payment_number"
style="@style/label_small"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:textColor="@color/black_300"
android:text="No. Pesanan: 123456789"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:orientation="vertical"
android:layout_marginEnd="16dp"
android:gravity="end">
<TextView
android:layout_width="150dp"
android:layout_height="wrap_content"
android:maxLines="1"
style="@style/label_small"
android:textAlignment="textEnd"
android:text="Konfirmasi pembayaran sebelum:"
android:textColor="@color/black_300" />
<TextView
android:id="@+id/tv_payment_due"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="25 Okt; 23.59"
style="@style/label_small"
android:paddingHorizontal="4dp"
android:textColor="@color/darkblue_500"
android:background="@drawable/bg_product_active" />
</LinearLayout>
<!-- Order Detail -->
<LinearLayout
android:id="@+id/layout_payment_detail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/layout_payment_header"
android:paddingHorizontal="16dp"
android:layout_marginTop="8dp"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/black_50"/>
<!-- Product Detail -->
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layout_payment_product_detail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingVertical="12dp">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/iv_payment_product"
android:layout_width="95dp"
android:layout_height="64dp"
android:src="@drawable/placeholder_image"
android:scaleType="centerCrop"
android:contentDescription="Payment Product Image"
app:shapeAppearanceOverlay="@style/store_product_image"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginStart="13dp"
app:layout_constraintStart_toEndOf="@id/iv_payment_product"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/tv_payment_product_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:text="Jaket Pink Fuschia"
style="@style/label_medium_prominent"/>
<TextView
android:id="@+id/tv_payment_product_variant"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:text="S"
style="@style/label_medium"
android:textColor="@color/black_300"/>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginStart="13dp"
android:gravity="end"
app:layout_constraintStart_toEndOf="@id/iv_payment_product"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@id/iv_payment_product">
<TextView
android:id="@+id/tv_payment_product_qty"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:text="x2"
style="@style/label_medium"
android:textColor="@color/black_300"
android:textAlignment="textEnd"/>
<TextView
android:id="@+id/tv_payment_product_price"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:text="Rp150.000"
style="@style/label_medium"
android:textAlignment="textEnd"/>
</LinearLayout>
<TextView
android:id="@+id/tv_see_more"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="true"
android:text="Lihat 3 produk lainnya"
android:gravity="center"
style="@style/label_small"
android:fontFamily="@font/dmsans_italic"
android:textColor="@color/black_300"
app:layout_constraintTop_toBottomOf="@id/iv_payment_product"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginVertical="8dp"
android:clickable="true"
android:visibility="gone"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/black_50"/>
<!-- Total Price -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingVertical="8dp">
<TextView
android:id="@+id/tv_payment_qty"
style="@style/label_large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:text="2 produk" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_alignParentEnd="true">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Total:"
style="@style/label_large_prominent"
android:textAlignment="textEnd"/>
<TextView
android:id="@+id/tv_payment_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Rp300.000"
style="@style/label_large_prominent"
android:textColor="@color/blue_500"
android:layout_marginStart="5dp"
android:textAlignment="textEnd"/>
</LinearLayout>
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/black_50"/>
<!-- Action Buttons -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="13dp"
android:gravity="center_vertical">
<LinearLayout
android:layout_width="158dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_alignParentStart="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:layout_width="16dp"
android:layout_height="16dp"
android:src="@drawable/ic_person"
app:tint="@color/black_500" />
<TextView
android:id="@+id/tv_payment_customer"
style="@style/label_small"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:layout_marginStart="4dp"
android:textColor="@color/black_500"
android:text="Gracia Hotmauli"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:layout_width="16dp"
android:layout_height="16dp"
android:src="@drawable/ic_location" />
<TextView
android:id="@+id/tv_payment_location"
style="@style/label_small"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:layout_marginStart="4dp"
android:textColor="@color/black_500"
android:text="Serang"/>
</LinearLayout>
</LinearLayout>
<Button
android:id="@+id/btn_confirm_payment"
style="@style/button.small.active.medium"
android:text="Konfirmasi Pembayaran"
android:layout_alignParentEnd="true"/>
</RelativeLayout>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="8dp"
android:background="@color/black_50"
android:layout_marginTop="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/layout_payment_detail"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,255 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:paddingTop="16dp">
<!-- Shipment Header -->
<View
android:id="@+id/shape_sells_title"
android:layout_width="4dp"
android:layout_height="32dp"
android:background="@drawable/shape_sells_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<LinearLayout
android:id="@+id/layout_shipment_header"
android:layout_width="220dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
app:layout_constraintStart_toStartOf="@id/shape_sells_title"
app:layout_constraintTop_toTopOf="parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_shipment_title"
style="@style/label_medium_prominent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:text="Pesanan Perlu Dikirim"/>
<TextView
android:id="@+id/tv_shipment_number"
style="@style/label_small"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:textColor="@color/black_300"
android:text="No. Pesanan: 123456789"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:orientation="vertical"
android:layout_marginEnd="16dp"
android:gravity="end">
<TextView
android:layout_width="150dp"
android:layout_height="wrap_content"
android:maxLines="1"
style="@style/label_small"
android:textAlignment="textEnd"
android:text="Kirim sebelum:"
android:textColor="@color/black_300" />
<TextView
android:id="@+id/tv_shipment_due"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="25 Okt; 23.59"
style="@style/label_small"
android:paddingHorizontal="4dp"
android:textColor="@color/darkblue_500"
android:background="@drawable/bg_product_active" />
</LinearLayout>
<!-- Order Detail -->
<LinearLayout
android:id="@+id/layout_shipment_detail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/layout_shipment_header"
android:paddingHorizontal="16dp"
android:layout_marginTop="8dp"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/black_50"/>
<!-- Product Detail -->
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layout_shipment_product_detail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingVertical="12dp">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/iv_shipment_product"
android:layout_width="95dp"
android:layout_height="48dp"
android:src="@drawable/placeholder_image"
android:scaleType="centerCrop"
android:contentDescription="Shipment Product Image"
app:shapeAppearanceOverlay="@style/store_product_image"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginStart="13dp"
app:layout_constraintStart_toEndOf="@id/iv_shipment_product"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/tv_shipment_product_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:text="Jaket Pink Fuschia"
style="@style/label_medium_prominent"/>
<TextView
android:id="@+id/tv_shipment_product_variant"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:text="S"
style="@style/label_medium"
android:textColor="@color/black_300"/>
</LinearLayout>
<TextView
android:id="@+id/tv_shipment_product_qty"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:maxLines="1"
android:text="x2"
style="@style/label_medium"
android:layout_marginStart="13dp"
android:gravity="end"
android:textAlignment="textEnd"
app:layout_constraintStart_toEndOf="@id/iv_shipment_product"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@id/iv_shipment_product"/>
<TextView
android:id="@+id/tv_see_more"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="true"
android:text="Lihat 3 produk lainnya"
android:gravity="center"
style="@style/label_small"
android:fontFamily="@font/dmsans_italic"
android:textColor="@color/black_300"
app:layout_constraintTop_toBottomOf="@id/iv_shipment_product"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginVertical="8dp"
android:clickable="true"
android:visibility="gone"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/black_50"/>
<!-- Action Buttons -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="13dp"
android:gravity="center_vertical">
<LinearLayout
android:layout_width="158dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_alignParentStart="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:layout_width="16dp"
android:layout_height="16dp"
android:src="@drawable/ic_person"
app:tint="@color/black_500" />
<TextView
android:id="@+id/tv_shipment_customer"
style="@style/label_small"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:layout_marginStart="4dp"
android:textColor="@color/black_500"
android:text="Gracia Hotmauli"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:layout_width="16dp"
android:layout_height="16dp"
android:src="@drawable/ic_location" />
<TextView
android:id="@+id/tv_shipment_location"
style="@style/label_small"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:layout_marginStart="4dp"
android:textColor="@color/black_500"
android:text="Serang"/>
</LinearLayout>
</LinearLayout>
<Button
android:id="@+id/btn_confirm_shipment"
style="@style/button.small.active.medium"
android:text="Kirim Pesanan"
android:layout_alignParentEnd="true"/>
</RelativeLayout>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="8dp"
android:background="@color/black_50"
android:layout_marginTop="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/layout_shipment_detail"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -75,7 +75,7 @@
</LinearLayout> </LinearLayout>
<ImageView <ImageView
android:id="@+id/ivMenu" android:id="@+id/iv_menu"
android:layout_width="24dp" android:layout_width="24dp"
android:layout_height="24dp" android:layout_height="24dp"
android:src="@drawable/ic_more_vertical" android:src="@drawable/ic_more_vertical"

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_edit_product"
android:title="Ubah Produk"
android:icon="@drawable/ic_edit"
app:showAsAction="ifRoom" />
<item
android:id="@+id/menu_delete_product"
android:title="Hapus Produk"
android:icon="@drawable/ic_delete"
app:showAsAction="ifRoom" />
</menu>

View File

@ -147,7 +147,7 @@
<!-- Buttons --> <!-- Buttons -->
<style name="button.large" parent="label_large_prominent"> <style name="button.large" parent="label_large_prominent">
<item name="android:padding">10.91dp</item> <item name="android:gravity">center</item>
<item name="android:layout_height">40dp</item> <item name="android:layout_height">40dp</item>
<item name="android:textAllCaps">false</item> <item name="android:textAllCaps">false</item>
<item name="backgroundTint">@null</item> <item name="backgroundTint">@null</item>
@ -217,7 +217,7 @@
</style> </style>
<style name="button.small" parent="label_medium_prominent"> <style name="button.small" parent="label_medium_prominent">
<item name="android:padding">7dp</item> <item name="android:padding">2dp</item>
<item name="android:layout_height">30dp</item> <item name="android:layout_height">30dp</item>
<item name="android:textAllCaps">false</item> <item name="android:textAllCaps">false</item>
<item name="backgroundTint">@null</item> <item name="backgroundTint">@null</item>

View File

@ -20,10 +20,12 @@ lifecycleViewmodelKtx = "2.8.7"
fragmentKtx = "1.5.6" fragmentKtx = "1.5.6"
navigationFragmentKtx = "2.8.5" navigationFragmentKtx = "2.8.5"
navigationUiKtx = "2.8.5" navigationUiKtx = "2.8.5"
recyclerview = "1.4.0"
[libraries] [libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-hilt-lifecycle-viewmodel = { module = "androidx.hilt:hilt-lifecycle-viewmodel", version.ref = "hiltLifecycleViewmodel" } androidx-hilt-lifecycle-viewmodel = { module = "androidx.hilt:hilt-lifecycle-viewmodel", version.ref = "hiltLifecycleViewmodel" }
androidx-recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerview" }
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hiltAndroid" } hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hiltAndroid" }
junit = { group = "junit", name = "junit", version.ref = "junit" } junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }