Compare commits

...

3 Commits

Author SHA1 Message Date
94bd32d6b0 Merge remote-tracking branch 'origin/master' 2025-08-19 07:32:25 +07:00
6baf4ee5ce fix add address 2025-08-19 07:27:48 +07:00
ff8654d12a fix wholesale price and edit profile 2025-08-18 16:01:07 +07:00
9 changed files with 91 additions and 67 deletions

View File

@ -151,7 +151,7 @@ class CartActivity : AppCompatActivity() {
// Debug log // Debug log
Log.d( Log.d(
TAG, TAG,
"cartItemId: ${updatedCartItem.cartItemId}, " + "cartItemId: ${updatedCartItem.cartItemId}, " +
"isWholesale: ${info.isWholesale}, " + "isWholesale: ${info.isWholesale}, " +
"wholesalePrice: $wholesalePrice, " + "wholesalePrice: $wholesalePrice, " +
@ -164,7 +164,17 @@ class CartActivity : AppCompatActivity() {
val cartItemIds = updatedItems.map { it.cartItem.cartItemId } val cartItemIds = updatedItems.map { it.cartItem.cartItemId }
val wholesaleArray = updatedItems.map { it.isWholesale }.toBooleanArray() val wholesaleArray = updatedItems.map { it.isWholesale }.toBooleanArray()
CheckoutActivity.startForCart(this, cartItemIds, wholesaleArray) // FIX: Pass wholesale prices as IntArray
val wholesalePricesArray = updatedItems.map { info ->
if (info.isWholesale) {
val wholesalePrice = wholesalePriceMap[info.cartItem.cartItemId]
wholesalePrice?.toInt() ?: info.cartItem.price
} else {
info.cartItem.price
}
}.toIntArray()
CheckoutActivity.startForCart(this, cartItemIds, wholesaleArray, wholesalePricesArray)
} }
private fun observeViewModel() { private fun observeViewModel() {

View File

@ -79,9 +79,6 @@ class CheckoutActivity : AppCompatActivity() {
// Determine if this is Buy Now or Cart checkout // Determine if this is Buy Now or Cart checkout
val isBuyNow = intent.hasExtra(EXTRA_PRODUCT_ID) && !intent.hasExtra(EXTRA_CART_ITEM_IDS) val isBuyNow = intent.hasExtra(EXTRA_PRODUCT_ID) && !intent.hasExtra(EXTRA_CART_ITEM_IDS)
val isWholesaleNow = intent.getBooleanExtra(EXTRA_ISWHOLESALE, false) val isWholesaleNow = intent.getBooleanExtra(EXTRA_ISWHOLESALE, false)
val wholesalePricesArray = intent.getIntArrayExtra(EXTRA_CART_ITEM_WHOLESALE_PRICES)
if (isBuyNow) { if (isBuyNow) {
// Process Buy Now flow // Process Buy Now flow
@ -99,8 +96,7 @@ class CheckoutActivity : AppCompatActivity() {
// Process Cart checkout flow // Process Cart checkout flow
val cartItemIds = intent.getIntArrayExtra(EXTRA_CART_ITEM_IDS)?.toList() ?: emptyList() val cartItemIds = intent.getIntArrayExtra(EXTRA_CART_ITEM_IDS)?.toList() ?: emptyList()
val isWholesaleArray = intent.getBooleanArrayExtra(EXTRA_CART_ITEM_WHOLESALE) val isWholesaleArray = intent.getBooleanArrayExtra(EXTRA_CART_ITEM_WHOLESALE)
val wholesalePricesArray = intent.getIntArrayExtra(EXTRA_CART_ITEM_WHOLESALE_PRICES)
if (cartItemIds.isNotEmpty()) { if (cartItemIds.isNotEmpty()) {
// Build map of cartItemId -> isWholesale // Build map of cartItemId -> isWholesale
@ -108,17 +104,18 @@ class CheckoutActivity : AppCompatActivity() {
cartItemIds.mapIndexed { index, id -> cartItemIds.mapIndexed { index, id ->
id to isWholesaleArray[index] id to isWholesaleArray[index]
}.toMap() }.toMap()
} else { } else {
emptyMap() emptyMap()
} }
// Build wholesalePriceMap - FIX: Map cartItemId to wholesale price
// Build wholesalePriceMap val wholesalePriceMap = if (wholesalePricesArray != null && wholesalePricesArray.size == cartItemIds.size) {
val wholesalePriceMap = cartItemIds.mapIndexed { index, id -> cartItemIds.mapIndexed { index, id ->
id to (wholesalePricesArray?.get(index) ?: 0) id to wholesalePricesArray[index]
}.toMap() }.toMap()
} else {
emptyMap()
}
viewModel.initializeFromCart( viewModel.initializeFromCart(
cartItemIds, cartItemIds,

View File

@ -96,7 +96,7 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
fun initializeFromCart( fun initializeFromCart(
cartItemIds: List<Int>, cartItemIds: List<Int>,
isWholesaleMap: Map<Int, Boolean> = emptyMap(), isWholesaleMap: Map<Int, Boolean> = emptyMap(),
wholesalePriceMap: Map<Int, Int> = emptyMap() // new wholesalePriceMap: Map<Int, Int> = emptyMap()
) { ) {
viewModelScope.launch { viewModelScope.launch {
_isLoading.value = true _isLoading.value = true
@ -111,13 +111,21 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
for (store in cartResult.data) { for (store in cartResult.data) {
val storeItems = store.cartItems.filter { it.cartItemId in cartItemIds } val storeItems = store.cartItems.filter { it.cartItemId in cartItemIds }
if (storeItems.isNotEmpty()) { if (storeItems.isNotEmpty()) {
// ✅ Override price with wholesale price if exists // ✅ Apply wholesale prices - Replace item prices with wholesale prices
val updatedItems = storeItems.map { item -> val updatedItems = storeItems.map { item ->
val wholesalePrice = wholesalePriceMap[item.cartItemId] val wholesalePrice = wholesalePriceMap[item.cartItemId]
if (wholesalePrice != null) { val isWholesale = isWholesaleMap[item.cartItemId] ?: false
item.copy(price = wholesalePrice.toInt())
} else item // Use wholesale price if item is wholesale and price exists
if (isWholesale && wholesalePrice != null) {
Log.d(TAG, "Applying wholesale price for item ${item.cartItemId}: ${item.price} -> $wholesalePrice")
item.copy(price = wholesalePrice)
} else {
Log.d(TAG, "Using regular price for item ${item.cartItemId}: ${item.price}")
item
}
} }
matchingItems.addAll(updatedItems) matchingItems.addAll(updatedItems)
storeData = store storeData = store
break break
@ -143,16 +151,17 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
sellerName = storeData.storeName, sellerName = storeData.storeName,
sellerId = storeData.storeId, sellerId = storeData.storeId,
isBuyNow = false, isBuyNow = false,
cartItems = matchingItems, cartItems = matchingItems, // These now have updated wholesale prices
cartItemWholesaleMap = isWholesaleMap cartItemWholesaleMap = isWholesaleMap
) )
Log.d(TAG, "CheckoutData initialized: ${_checkoutData.value}")
Log.d(TAG, "Matching cart items: ${matchingItems.size}") Log.d(TAG, "CheckoutData initialized with ${matchingItems.size} items")
Log.d(TAG, "Cart items: ${matchingItems.map { it.productName to it.price }}") matchingItems.forEachIndexed { index, item ->
Log.d(TAG, "IsWholesaleMap passed: $isWholesaleMap") val isWholesale = isWholesaleMap[item.cartItemId] ?: false
Log.d(TAG, "WholesalePriceMap passed: $wholesalePriceMap") Log.d(TAG, "Item $index: ${item.productName}, Price: ${item.price}, IsWholesale: $isWholesale")
}
// Calculate totals with updated prices
calculateSubtotal() calculateSubtotal()
calculateTotal() calculateTotal()
} else { } else {
@ -163,11 +172,11 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
} }
} catch (e: Exception) { } catch (e: Exception) {
_errorMessage.value = "Error: ${e.message}" _errorMessage.value = "Error: ${e.message}"
Log.e(TAG, "Error in initializeFromCart", e)
} finally { } finally {
_isLoading.value = false _isLoading.value = false
} }
} }
} }
fun getPaymentMethods() { fun getPaymentMethods() {
@ -418,8 +427,6 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
} }
} }
companion object { companion object {
private const val TAG = "CheckoutViewModel" private const val TAG = "CheckoutViewModel"
} }

View File

@ -34,6 +34,8 @@ class SingleCartItemAdapter(private val cartItem: CartItemsItem) :
// Load placeholder image // Load placeholder image
Glide.with(ivProduct.context) Glide.with(ivProduct.context)
.load(R.drawable.placeholder_image) .load(R.drawable.placeholder_image)
.placeholder(R.drawable.placeholder_image)
.error(R.drawable.placeholder_image)
.into(ivProduct) .into(ivProduct)
} }
} }

View File

@ -22,7 +22,6 @@ import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import com.alya.ecommerce_serang.data.api.dto.CreateAddressRequest import com.alya.ecommerce_serang.data.api.dto.CreateAddressRequest
import com.alya.ecommerce_serang.data.api.dto.UserProfile
import com.alya.ecommerce_serang.data.api.response.customer.order.CitiesItem import com.alya.ecommerce_serang.data.api.response.customer.order.CitiesItem
import com.alya.ecommerce_serang.data.api.response.customer.order.ProvincesItem import com.alya.ecommerce_serang.data.api.response.customer.order.ProvincesItem
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
@ -37,8 +36,8 @@ class AddAddressActivity : AppCompatActivity() {
private lateinit var binding: ActivityAddAddressBinding private lateinit var binding: ActivityAddAddressBinding
private lateinit var apiService: ApiService private lateinit var apiService: ApiService
private lateinit var sessionManager: SessionManager private lateinit var sessionManager: SessionManager
private var profileUser: Int = 1
private lateinit var locationManager: LocationManager private lateinit var locationManager: LocationManager
private var profileUserId: Int? = null
private var isRequestingLocation = false private var isRequestingLocation = false
@ -80,11 +79,15 @@ class AddAddressActivity : AppCompatActivity() {
) )
windowInsets windowInsets
} }
viewModel.loadUserProfile()
// Get user profile from session manager viewModel.userProfile.observe(this) { user ->
// profileUser =UserProfile. if (user != null) {
viewModel.userProfile.observe(this){ user -> profileUserId = user.userId
user?.let { updateProfile(it) } Log.d(TAG, "Fetched userId = $profileUserId") // ✅ debug log
} else {
Log.e(TAG, "Error get profile")
}
} }
setupToolbar() setupToolbar()
@ -94,16 +97,10 @@ class AddAddressActivity : AppCompatActivity() {
setupButtonListeners() setupButtonListeners()
setupObservers() setupObservers()
// Force trigger province loading to ensure it happens // Force trigger province loading to ensure it happens
viewModel.getProvinces() viewModel.getProvinces()
} }
private fun updateProfile(userProfile: UserProfile){
profileUser = userProfile.userId
}
// UI setup methods // UI setup methods
private fun setupToolbar() { private fun setupToolbar() {
binding.toolbar.setNavigationOnClickListener { binding.toolbar.setNavigationOnClickListener {
@ -280,12 +277,7 @@ class AddAddressActivity : AppCompatActivity() {
val postalCode = binding.etKodePos.text.toString().trim() val postalCode = binding.etKodePos.text.toString().trim()
val recipient = binding.etNamaPenerima.text.toString().trim() val recipient = binding.etNamaPenerima.text.toString().trim()
val phone = binding.etNomorHp.text.toString().trim() val phone = binding.etNomorHp.text.toString().trim()
val userId = try { val userId = profileUserId
profileUser
} catch (e: Exception) {
Log.w(TAG, "Error getting userId, using default", e)
1 // Default userId for testing
}
val isStoreLocation = false val isStoreLocation = false
val provinceId = viewModel.selectedProvinceId val provinceId = viewModel.selectedProvinceId
@ -333,7 +325,7 @@ class AddAddressActivity : AppCompatActivity() {
// Create request with all fields // Create request with all fields
val request = CreateAddressRequest( val request = CreateAddressRequest(
userId = userId, userId = userId!!,
lat = latitude!!, // Safe to use !! as we've checked above lat = latitude!!, // Safe to use !! as we've checked above
long = longitude!!, long = longitude!!,
street = street, street = street,
@ -525,6 +517,6 @@ class AddAddressActivity : AppCompatActivity() {
} }
companion object { companion object {
private const val TAG = "AddAddressViewModel" private const val TAG = "AddAddressActivity"
} }
} }

View File

@ -252,6 +252,24 @@ class AddAddressViewModel(private val repository: OrderRepository, private val u
return params return params
} }
fun loadUserProfile() {
viewModelScope.launch {
when (val result = repository.fetchUserProfile()) {
is Result.Success -> {
result.data?.let {
_userProfile.postValue(it) // send UserProfile to LiveData
} ?: _errorMessageUser.postValue("User data not found")
}
is Result.Error -> {
_errorMessageUser.postValue(result.exception.message ?: "Unknown error")
}
is Result.Loading -> {
null
}
}
}
}
companion object { companion object {
private const val TAG = "AddAddressViewModel" private const val TAG = "AddAddressViewModel"
} }

View File

@ -1,10 +1,12 @@
package com.alya.ecommerce_serang.ui.profile.editprofile package com.alya.ecommerce_serang.ui.profile.editprofile
import android.Manifest
import android.app.Activity import android.app.Activity
import android.app.DatePickerDialog import android.app.DatePickerDialog
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.provider.MediaStore import android.provider.MediaStore
import android.util.Log import android.util.Log
@ -159,17 +161,14 @@ class EditProfileCustActivity : AppCompatActivity() {
} }
private fun openImagePicker() { private fun openImagePicker() {
// Check for permission first val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission( Manifest.permission.READ_MEDIA_IMAGES
this, } else {
android.Manifest.permission.READ_EXTERNAL_STORAGE Manifest.permission.READ_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED }
) {
ActivityCompat.requestPermissions( if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
this, ActivityCompat.requestPermissions(this, arrayOf(permission), REQUEST_STORAGE_PERMISSION)
arrayOf(android.Manifest.permission.READ_EXTERNAL_STORAGE),
REQUEST_STORAGE_PERMISSION
)
} else { } else {
launchImagePicker() launchImagePicker()
} }

View File

@ -17,6 +17,7 @@ import com.alya.ecommerce_serang.ui.profile.mystore.sells.shipment.DetailShipmen
import com.alya.ecommerce_serang.utils.viewmodel.SellsViewModel import com.alya.ecommerce_serang.utils.viewmodel.SellsViewModel
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.google.gson.Gson import com.google.gson.Gson
import java.text.NumberFormat
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Calendar import java.util.Calendar
import java.util.Locale import java.util.Locale
@ -96,7 +97,7 @@ class SellsAdapter(
val product = order.orderItems?.firstOrNull() val product = order.orderItems?.firstOrNull()
tvSellsProductName.text = product?.productName tvSellsProductName.text = product?.productName
tvSellsProductQty.text = "x${product?.quantity}" tvSellsProductQty.text = "x${product?.quantity}"
tvSellsProductPrice.text = formatPrice(product?.price.toString()) tvSellsProductPrice.text = product?.price?.let { formatPrice(it.toInt()) }
val fullImageUrl = when (val img = product?.productImage) { val fullImageUrl = when (val img = product?.productImage) {
is String -> { is String -> {
@ -169,7 +170,7 @@ class SellsAdapter(
val product = order.orderItems?.firstOrNull() val product = order.orderItems?.firstOrNull()
tvSellsProductName.text = product?.productName tvSellsProductName.text = product?.productName
tvSellsProductQty.text = "x${product?.quantity}" tvSellsProductQty.text = "x${product?.quantity}"
tvSellsProductPrice.text = formatPrice(product?.price.toString()) tvSellsProductPrice.text = product?.price?.let { formatPrice(it.toInt()) }
val fullImageUrl = when (val img = product?.productImage) { val fullImageUrl = when (val img = product?.productImage) {
is String -> { is String -> {
@ -185,7 +186,7 @@ class SellsAdapter(
.into(ivSellsProduct) .into(ivSellsProduct)
tvSellsQty.text = "${order.orderItems?.size} produk" tvSellsQty.text = "${order.orderItems?.size} produk"
tvSellsPrice.text = formatPrice(order.totalAmount.toString()) tvSellsPrice.text = order.totalAmount?.let { formatPrice(it.toInt()) }
} }
"paid" -> { "paid" -> {
layoutOrders.visibility = View.GONE layoutOrders.visibility = View.GONE
@ -308,10 +309,9 @@ class SellsAdapter(
} }
} }
private fun formatPrice(price: String): String { private fun formatPrice(amount: Int): String {
val priceDouble = price.toDoubleOrNull() ?: 0.0 val formatter = NumberFormat.getCurrencyInstance(Locale("in", "ID"))
val formattedPrice = String.format(Locale("id", "ID"), "Rp%,.0f", priceDouble) return formatter.format(amount.toLong()).replace(",00", "")
return formattedPrice
} }
} }
} }

View File

@ -38,7 +38,6 @@
android:src="@drawable/baseline_edit_24" android:src="@drawable/baseline_edit_24"
android:focusable="true" android:focusable="true"
android:clickable="true" android:clickable="true"
android:visibility="gone"
android:layout_marginHorizontal="16dp" android:layout_marginHorizontal="16dp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"/> app:layout_constraintTop_toTopOf="parent"/>