mirror of
https://github.com/shaulascr/ecommerce_serang.git
synced 2025-08-12 18:22:22 +00:00
update cart wholesale
This commit is contained in:
@ -78,13 +78,16 @@ data class Product(
|
|||||||
val preorderDuration: String? = null
|
val preorderDuration: String? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
data class CartItemWholesaleInfo(
|
|
||||||
val cartItemId: Int,
|
|
||||||
val isWholesale: Boolean,
|
|
||||||
val wholesalePrice: Double? = null
|
|
||||||
)
|
|
||||||
|
|
||||||
data class CartItemCheckoutInfo(
|
data class CartItemCheckoutInfo(
|
||||||
val cartItem: CartItemsItem,
|
val cartItem: CartItemsItem,
|
||||||
val isWholesale: Boolean
|
val isWholesale: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
data class CartItemWholesaleInfo(
|
||||||
|
val cartItemId: Int,
|
||||||
|
val isWholesale: Boolean,
|
||||||
|
val wholesalePrice: Double? = null
|
||||||
|
)
|
@ -91,7 +91,6 @@ class CartActivity : AppCompatActivity() {
|
|||||||
adapter = storeAdapter
|
adapter = storeAdapter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupListeners() {
|
private fun setupListeners() {
|
||||||
binding.cbSelectAll.setOnCheckedChangeListener { _, _ ->
|
binding.cbSelectAll.setOnCheckedChangeListener { _, _ ->
|
||||||
viewModel.toggleSelectAll()
|
viewModel.toggleSelectAll()
|
||||||
@ -99,16 +98,20 @@ class CartActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
binding.btnCheckout.setOnClickListener {
|
binding.btnCheckout.setOnClickListener {
|
||||||
if (viewModel.totalSelectedCount.value ?: 0 > 0) {
|
if (viewModel.totalSelectedCount.value ?: 0 > 0) {
|
||||||
val selectedItems = viewModel.prepareCheckout()
|
if (viewModel.hasConsistentWholesaleStatus.value == true) {
|
||||||
if (selectedItems.isNotEmpty()) {
|
val selectedItems = viewModel.prepareCheckout()
|
||||||
// Check if all items are from the same store
|
if (selectedItems.isNotEmpty()) {
|
||||||
val storeId = viewModel.activeStoreId.value
|
// Check if all items are from the same store
|
||||||
if (storeId != null) {
|
val storeId = viewModel.activeStoreId.value
|
||||||
// Start checkout with the prepared items
|
if (storeId != null) {
|
||||||
startCheckoutWithWholesaleInfo(selectedItems)
|
// Start checkout with the prepared items
|
||||||
} else {
|
startCheckoutWithWholesaleInfo(selectedItems)
|
||||||
Toast.makeText(this, "Please select items from a single store only", Toast.LENGTH_SHORT).show()
|
} else {
|
||||||
|
Toast.makeText(this, "Please select items from a single store only", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, "Tidak dapat checkout produk grosir dan retail sekaligus", Toast.LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(this, "Pilih produk terlebih dahulu", Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, "Pilih produk terlebih dahulu", Toast.LENGTH_SHORT).show()
|
||||||
@ -176,6 +179,30 @@ class CartActivity : AppCompatActivity() {
|
|||||||
viewModel.toggleSelectAll()
|
viewModel.toggleSelectAll()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
viewModel.hasConsistentWholesaleStatus.observe(this) { isConsistent ->
|
||||||
|
if (!isConsistent && (viewModel.totalSelectedCount.value ?: 0) > 1) {
|
||||||
|
binding.btnCheckout.isEnabled = false
|
||||||
|
// Show an error message or indicator
|
||||||
|
binding.tvWholesaleWarning.visibility = View.VISIBLE
|
||||||
|
binding.tvWholesaleWarning.text = "Tidak dapat checkout produk grosir dan retail sekaligus"
|
||||||
|
} else {
|
||||||
|
binding.btnCheckout.isEnabled = true
|
||||||
|
binding.tvWholesaleWarning.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.cartItemWholesaleStatus.observe(this) { wholesaleStatusMap ->
|
||||||
|
viewModel.cartItemWholesalePrice.value?.let { wholesalePriceMap ->
|
||||||
|
storeAdapter.updateWholesaleStatus(wholesaleStatusMap, wholesalePriceMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.cartItemWholesalePrice.observe(this) { wholesalePriceMap ->
|
||||||
|
viewModel.cartItemWholesaleStatus.value?.let { wholesaleStatusMap ->
|
||||||
|
storeAdapter.updateWholesaleStatus(wholesaleStatusMap, wholesalePriceMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showEmptyState(isEmpty: Boolean) {
|
private fun showEmptyState(isEmpty: Boolean) {
|
||||||
|
@ -49,6 +49,9 @@ class CartViewModel(private val repository: OrderRepository) : ViewModel() {
|
|||||||
private val _cartItemWholesalePrice = MutableLiveData<Map<Int, Double>>(mapOf())
|
private val _cartItemWholesalePrice = MutableLiveData<Map<Int, Double>>(mapOf())
|
||||||
val cartItemWholesalePrice: LiveData<Map<Int, Double>> = _cartItemWholesalePrice
|
val cartItemWholesalePrice: LiveData<Map<Int, Double>> = _cartItemWholesalePrice
|
||||||
|
|
||||||
|
private val _hasConsistentWholesaleStatus = MutableLiveData<Boolean>(true)
|
||||||
|
val hasConsistentWholesaleStatus: LiveData<Boolean> = _hasConsistentWholesaleStatus
|
||||||
|
|
||||||
fun getCart() {
|
fun getCart() {
|
||||||
_isLoading.value = true
|
_isLoading.value = true
|
||||||
_errorMessage.value = null
|
_errorMessage.value = null
|
||||||
@ -379,4 +382,26 @@ class CartViewModel(private val repository: OrderRepository) : ViewModel() {
|
|||||||
calculateTotalPrice()
|
calculateTotalPrice()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun checkWholesaleConsistency() {
|
||||||
|
val selectedItemIds = _selectedItems.value ?: HashSet()
|
||||||
|
val wholesaleStatus = _cartItemWholesaleStatus.value ?: mapOf()
|
||||||
|
|
||||||
|
if (selectedItemIds.isEmpty()) {
|
||||||
|
_hasConsistentWholesaleStatus.value = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the wholesale status of the first selected item
|
||||||
|
val firstSelectedId = selectedItemIds.first()
|
||||||
|
val firstStatus = wholesaleStatus[firstSelectedId] ?: false
|
||||||
|
|
||||||
|
// Check if all other selected items have the same wholesale status
|
||||||
|
val allSameStatus = selectedItemIds.all { itemId ->
|
||||||
|
val itemStatus = wholesaleStatus[itemId] ?: false
|
||||||
|
itemStatus == firstStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
_hasConsistentWholesaleStatus.value = allSameStatus
|
||||||
|
}
|
||||||
}
|
}
|
@ -28,6 +28,8 @@ class StoreAdapter(
|
|||||||
private var selectedItems = HashSet<Int>()
|
private var selectedItems = HashSet<Int>()
|
||||||
private var selectedStores = HashSet<Int>()
|
private var selectedStores = HashSet<Int>()
|
||||||
private var activeStoreId: Int? = null
|
private var activeStoreId: Int? = null
|
||||||
|
private var wholesaleStatusMap: Map<Int, Boolean> = mapOf()
|
||||||
|
private var wholesalePriceMap: Map<Int, Double> = mapOf()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val VIEW_TYPE_STORE = 0
|
private const val VIEW_TYPE_STORE = 0
|
||||||
@ -70,6 +72,27 @@ class StoreAdapter(
|
|||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun isItemSelectable(cartItemId: Int): Boolean {
|
||||||
|
// If no items are selected, any item can be selected
|
||||||
|
if (selectedItems.isEmpty()) return true
|
||||||
|
|
||||||
|
// Get wholesale status of this item
|
||||||
|
val thisItemWholesale = wholesaleStatusMap[cartItemId] ?: false
|
||||||
|
|
||||||
|
// Get wholesale status of first selected item
|
||||||
|
val firstSelectedId = selectedItems.first()
|
||||||
|
val firstSelectedWholesale = wholesaleStatusMap[firstSelectedId] ?: false
|
||||||
|
|
||||||
|
// Item can be selected if its wholesale status matches the first selected item
|
||||||
|
return thisItemWholesale == firstSelectedWholesale
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateWholesaleStatus(wholesaleStatusMap: Map<Int, Boolean>, wholesalePriceMap: Map<Int, Double>) {
|
||||||
|
this.wholesaleStatusMap = wholesaleStatusMap
|
||||||
|
this.wholesalePriceMap = wholesalePriceMap
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
return when (viewType) {
|
return when (viewType) {
|
||||||
VIEW_TYPE_STORE -> {
|
VIEW_TYPE_STORE -> {
|
||||||
@ -100,11 +123,16 @@ class StoreAdapter(
|
|||||||
val cartItem = store.cartItems[itemIndex]
|
val cartItem = store.cartItems[itemIndex]
|
||||||
val isSelected = selectedItems.contains(cartItem.cartItemId)
|
val isSelected = selectedItems.contains(cartItem.cartItemId)
|
||||||
val isEnabled = activeStoreId == null || activeStoreId == store.storeId
|
val isEnabled = activeStoreId == null || activeStoreId == store.storeId
|
||||||
|
val isItemWholesale = wholesaleStatusMap[cartItem.cartItemId] ?: false
|
||||||
|
val wholesalePrice = wholesalePriceMap[cartItem.cartItemId]
|
||||||
|
val isSelectable = isItemSelectable(cartItem.cartItemId)
|
||||||
|
|
||||||
holder.bind(
|
holder.bind(
|
||||||
cartItem,
|
cartItem,
|
||||||
isSelected,
|
isSelected,
|
||||||
isEnabled,
|
isEnabled && isSelectable,
|
||||||
|
isItemWholesale,
|
||||||
|
wholesalePrice,
|
||||||
{ isChecked -> onItemCheckChanged(cartItem.cartItemId, store.storeId, isChecked) },
|
{ isChecked -> onItemCheckChanged(cartItem.cartItemId, store.storeId, isChecked) },
|
||||||
{ quantity -> onItemQuantityChanged(cartItem.cartItemId, quantity) },
|
{ quantity -> onItemQuantityChanged(cartItem.cartItemId, quantity) },
|
||||||
{ onItemDeleted(cartItem.cartItemId) }
|
{ onItemDeleted(cartItem.cartItemId) }
|
||||||
@ -158,20 +186,67 @@ class StoreAdapter(
|
|||||||
private val btnMinus: ImageButton = itemView.findViewById(R.id.btnMinus)
|
private val btnMinus: ImageButton = itemView.findViewById(R.id.btnMinus)
|
||||||
private val tvQuantity: TextView = itemView.findViewById(R.id.tvQuantity)
|
private val tvQuantity: TextView = itemView.findViewById(R.id.tvQuantity)
|
||||||
private val btnPlus: ImageButton = itemView.findViewById(R.id.btnPlus)
|
private val btnPlus: ImageButton = itemView.findViewById(R.id.btnPlus)
|
||||||
|
private val tvWholesaleIndicator : TextView = itemView.findViewById((R.id.tvWholesaleIndicator))
|
||||||
private val quantityController: ConstraintLayout = itemView.findViewById(R.id.quantityController)
|
private val quantityController: ConstraintLayout = itemView.findViewById(R.id.quantityController)
|
||||||
|
|
||||||
fun bind(
|
fun bind(
|
||||||
cartItem: CartItemsItem,
|
cartItem: CartItemsItem,
|
||||||
isChecked: Boolean,
|
isSelected: Boolean,
|
||||||
isEnabled: Boolean,
|
isEnabled: Boolean,
|
||||||
|
isWholesale: Boolean,
|
||||||
|
wholesalePrice: Double?,
|
||||||
onCheckedChange: (Boolean) -> Unit,
|
onCheckedChange: (Boolean) -> Unit,
|
||||||
onQuantityChanged: (Int) -> Unit,
|
onQuantityChanged: (Int) -> Unit,
|
||||||
onDelete: () -> Unit
|
onDelete: () -> Unit
|
||||||
) {
|
) {
|
||||||
|
// Set product name
|
||||||
tvProductName.text = cartItem.productName
|
tvProductName.text = cartItem.productName
|
||||||
tvPrice.text = formatCurrency(cartItem.price)
|
|
||||||
|
// Set price based on wholesale status
|
||||||
|
if (isWholesale && wholesalePrice != null) {
|
||||||
|
tvPrice.text = formatCurrency(wholesalePrice.toInt())
|
||||||
|
// Show wholesale indicator if available
|
||||||
|
tvWholesaleIndicator?.visibility = View.VISIBLE
|
||||||
|
tvWholesaleIndicator?.text = "Harga Grosir"
|
||||||
|
} else {
|
||||||
|
tvPrice.text = formatCurrency(cartItem.price)
|
||||||
|
tvWholesaleIndicator?.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set quantity
|
||||||
tvQuantity.text = cartItem.quantity.toString()
|
tvQuantity.text = cartItem.quantity.toString()
|
||||||
|
|
||||||
|
// Visual indication for wholesale items
|
||||||
|
// if (isWholesale) {
|
||||||
|
// // You can add a background or border to indicate wholesale items
|
||||||
|
// // For example:
|
||||||
|
//// itemView.setBackgroundResource(R.drawable.bg_wholesale_item)
|
||||||
|
// // If you don't have this drawable, you can use a simple color tint instead:
|
||||||
|
// itemView.setBackgroundColor(ContextCompat.getColor(itemView.context, R.color.wholesale_item_bg))
|
||||||
|
// } else {
|
||||||
|
// // Reset to default background
|
||||||
|
//// itemView.setBackgroundResource(R.drawable.bg_normal_item)
|
||||||
|
// // Or if you don't have this drawable:
|
||||||
|
// itemView.setBackgroundColor(ContextCompat.getColor(itemView.context, R.color.normal_item_bg))
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Set checkbox state without triggering listener
|
||||||
|
cbItem.setOnCheckedChangeListener(null)
|
||||||
|
cbItem.isChecked = isSelected
|
||||||
|
cbItem.isEnabled = isEnabled
|
||||||
|
|
||||||
|
// If not enabled, visually indicate this item can't be selected
|
||||||
|
if (!isEnabled) {
|
||||||
|
itemView.alpha = 0.5f
|
||||||
|
} else {
|
||||||
|
itemView.alpha = 1.0f
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set checkbox listener
|
||||||
|
cbItem.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
onCheckedChange(isChecked)
|
||||||
|
}
|
||||||
|
|
||||||
// Load product image
|
// Load product image
|
||||||
Glide.with(itemView.context)
|
Glide.with(itemView.context)
|
||||||
.load("https://example.com/images/${cartItem.productId}.jpg") // Assume image URL based on product ID
|
.load("https://example.com/images/${cartItem.productId}.jpg") // Assume image URL based on product ID
|
||||||
@ -179,15 +254,6 @@ class StoreAdapter(
|
|||||||
.error(R.drawable.placeholder_image)
|
.error(R.drawable.placeholder_image)
|
||||||
.into(ivProduct)
|
.into(ivProduct)
|
||||||
|
|
||||||
// Set checkbox state without triggering listener
|
|
||||||
cbItem.setOnCheckedChangeListener(null)
|
|
||||||
cbItem.isChecked = isChecked
|
|
||||||
cbItem.isEnabled = isEnabled
|
|
||||||
|
|
||||||
cbItem.setOnCheckedChangeListener { _, isChecked ->
|
|
||||||
onCheckedChange(isChecked)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Quantity control
|
// Quantity control
|
||||||
btnMinus.setOnClickListener {
|
btnMinus.setOnClickListener {
|
||||||
val currentQty = tvQuantity.text.toString().toInt()
|
val currentQty = tvQuantity.text.toString().toInt()
|
||||||
|
12
app/src/main/res/drawable/btn_minus_24dp.xml
Normal file
12
app/src/main/res/drawable/btn_minus_24dp.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M20,12H12H4"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
@ -21,6 +21,16 @@
|
|||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/header"/>
|
app:layout_constraintTop_toBottomOf="@+id/header"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvWholesaleWarning"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="#F44336"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/bottomCheckoutLayout" />
|
||||||
|
|
||||||
<!-- Bottom Checkout Layout -->
|
<!-- Bottom Checkout Layout -->
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/bottomCheckoutLayout"
|
android:id="@+id/bottomCheckoutLayout"
|
||||||
@ -64,6 +74,8 @@
|
|||||||
app:layout_constraintStart_toEndOf="@+id/tvTotalLabel"
|
app:layout_constraintStart_toEndOf="@+id/tvTotalLabel"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/btnCheckout"
|
android:id="@+id/btnCheckout"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
@ -79,7 +79,7 @@
|
|||||||
android:layout_width="30dp"
|
android:layout_width="30dp"
|
||||||
android:layout_height="30dp"
|
android:layout_height="30dp"
|
||||||
android:background="@drawable/bg_button_filled"
|
android:background="@drawable/bg_button_filled"
|
||||||
android:src="@drawable/baseline_add_24"
|
android:src="@drawable/btn_minus_24dp"
|
||||||
app:tint="@color/white"/>
|
app:tint="@color/white"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
@ -54,6 +54,17 @@
|
|||||||
app:layout_constraintStart_toStartOf="@+id/tvProductName"
|
app:layout_constraintStart_toStartOf="@+id/tvProductName"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/tvProductName" />
|
app:layout_constraintTop_toBottomOf="@+id/tvProductName" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvWholesaleIndicator"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Harga Grosir"
|
||||||
|
android:textColor="@color/blue_500"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/tvPrice"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/tvPrice" />
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/quantityController"
|
android:id="@+id/quantityController"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
Reference in New Issue
Block a user