fix address and payment

This commit is contained in:
shaulascr
2025-04-12 18:02:00 +07:00
parent fd3ff60037
commit c420966f6a
7 changed files with 205 additions and 92 deletions

1
.idea/misc.xml generated
View File

@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CodeInsightWorkspaceSettings">
<option name="optimizeImportsOnTheFly" value="true" />

View File

@ -3,11 +3,15 @@ package com.alya.ecommerce_serang.ui.order
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.ViewGroup
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.alya.ecommerce_serang.data.api.dto.CheckoutData
import com.alya.ecommerce_serang.data.api.dto.OrderRequest
import com.alya.ecommerce_serang.data.api.dto.OrderRequestBuy
@ -42,6 +46,7 @@ class CheckoutActivity : AppCompatActivity() {
sessionManager = SessionManager(this)
// Setup UI components
setupToolbar()
setupObservers()
@ -74,6 +79,11 @@ class CheckoutActivity : AppCompatActivity() {
finish()
}
}
viewModel.getPaymentMethods { paymentMethods ->
// Logging is just for debugging
Log.d("CheckoutActivity", "Loaded ${paymentMethods.size} payment methods")
}
}
private fun setupToolbar() {
@ -87,13 +97,6 @@ class CheckoutActivity : AppCompatActivity() {
viewModel.checkoutData.observe(this) { data ->
setupProductRecyclerView(data)
updateOrderSummary()
// Load payment methods
viewModel.getPaymentMethods { paymentMethods ->
if (paymentMethods.isNotEmpty()) {
setupPaymentMethodsRecyclerView(paymentMethods)
}
}
}
// Observe address details
@ -102,14 +105,24 @@ class CheckoutActivity : AppCompatActivity() {
binding.tvAddress.text = "${address?.street}, ${address?.subdistrict}"
}
// Observe payment details
viewModel.paymentDetails.observe(this) { payment ->
if (payment != null) {
// Update selected payment in adapter by name instead of ID
paymentAdapter?.setSelectedPaymentName(payment.name)
viewModel.availablePaymentMethods.observe(this) { paymentMethods ->
if (paymentMethods.isNotEmpty()) {
setupPaymentMethodsRecyclerView(paymentMethods)
}
}
// Observe selected payment
viewModel.selectedPayment.observe(this) { selectedPayment ->
if (selectedPayment != null) {
// Update the adapter to show the selected payment
paymentAdapter?.setSelectedPaymentName(selectedPayment.name)
// Optional: Update other UI elements to show the selected payment
// For example: binding.tvSelectedPaymentMethod.text = selectedPayment.name
}
}
// Observe loading state
viewModel.isLoading.observe(this) { isLoading ->
binding.btnPay.isEnabled = !isLoading
@ -133,6 +146,47 @@ class CheckoutActivity : AppCompatActivity() {
}
}
private fun setupPaymentMethodsRecyclerView(paymentMethods: List<PaymentInfoItem>) {
paymentAdapter = PaymentMethodAdapter(paymentMethods) { payment ->
// Convert payment name to ID
val paymentId = payment.name.toIntOrNull() ?: 0
// Call the ViewModel's setPaymentMethod function
viewModel.setPaymentMethod(paymentId)
}
binding.rvPaymentMethods.apply {
layoutManager = LinearLayoutManager(this@CheckoutActivity)
adapter = paymentAdapter
}
}
private fun updatePaymentMethodsAdapter(paymentMethods: List<PaymentInfoItem>, selectedId: Int?) {
Log.d("CheckoutActivity", "Updating payment adapter with ${paymentMethods.size} methods")
// Simple test adapter
val testAdapter = object : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val textView = TextView(parent.context)
textView.setPadding(16, 16, 16, 16)
textView.textSize = 16f
return object : RecyclerView.ViewHolder(textView) {}
}
override fun getItemCount() = paymentMethods.size
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val payment = paymentMethods[position]
(holder.itemView as TextView).text = "Payment: ${payment.name}"
}
}
binding.rvPaymentMethods.apply {
layoutManager = LinearLayoutManager(this@CheckoutActivity)
adapter = testAdapter
}
}
private fun setupProductRecyclerView(checkoutData: CheckoutData) {
val adapter = if (checkoutData.isBuyNow || checkoutData.cartItems.size <= 1) {
CheckoutSellerAdapter(checkoutData)
@ -147,21 +201,6 @@ class CheckoutActivity : AppCompatActivity() {
}
}
private fun setupPaymentMethodsRecyclerView(paymentMethods: List<PaymentInfoItem>) {
paymentAdapter = PaymentMethodAdapter(paymentMethods) { payment ->
// When a payment method is selected
// Since PaymentInfoItem doesn't have an id field, we'll use the name as identifier
// You might need to convert the name to an ID if your backend expects an integer
val paymentId = payment.name.toIntOrNull() ?: 0
viewModel.setPaymentMethod(paymentId)
}
binding.rvPaymentMethods.apply {
layoutManager = LinearLayoutManager(this@CheckoutActivity)
adapter = paymentAdapter
}
}
private fun updateOrderSummary() {
viewModel.checkoutData.value?.let { data ->
// Update price information
@ -251,6 +290,9 @@ class CheckoutActivity : AppCompatActivity() {
val addressId = result.data?.getIntExtra(AddressActivity.EXTRA_ADDRESS_ID, 0) ?: 0
if (addressId > 0) {
viewModel.setSelectedAddress(addressId)
// You might want to show a toast or some UI feedback
Toast.makeText(this, "Address selected successfully", Toast.LENGTH_SHORT).show()
}
}
}
@ -299,7 +341,7 @@ class CheckoutActivity : AppCompatActivity() {
}
// Check if payment method is selected
if (viewModel.paymentDetails.value == null) {
if (viewModel.selectedPayment.value == null) {
Toast.makeText(this, "Silakan pilih metode pembayaran", Toast.LENGTH_SHORT).show()
return false
}

View File

@ -24,8 +24,12 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
private val _addressDetails = MutableLiveData<AddressesItem?>()
val addressDetails: LiveData<AddressesItem?> = _addressDetails
private val _paymentDetails = MutableLiveData<PaymentInfoItem?>()
val paymentDetails: LiveData<PaymentInfoItem?> = _paymentDetails
private val _availablePaymentMethods = MutableLiveData<List<PaymentInfoItem>>()
val availablePaymentMethods: LiveData<List<PaymentInfoItem>> = _availablePaymentMethods
// Selected payment method
private val _selectedPayment = MutableLiveData<PaymentInfoItem?>()
val selectedPayment: LiveData<PaymentInfoItem?> = _selectedPayment
private val _isLoading = MutableLiveData<Boolean>()
val isLoading: LiveData<Boolean> = _isLoading
@ -144,7 +148,6 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
}
}
// Get payment methods from API
fun getPaymentMethods(callback: (List<PaymentInfoItem>) -> Unit) {
viewModelScope.launch {
try {
@ -154,17 +157,74 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
val storeResult = repository.fetchStoreDetail(storeId)
if (storeResult is Result.Success && storeResult.data != null) {
callback(storeResult.data.paymentInfo)
val paymentMethodsList = storeResult.data.paymentInfo
_availablePaymentMethods.value = paymentMethodsList
callback(paymentMethodsList)
} else {
_availablePaymentMethods.value = emptyList()
callback(emptyList())
}
} catch (e: Exception) {
Log.e(TAG, "Error fetching payment methods", e)
_availablePaymentMethods.value = emptyList()
callback(emptyList())
}
}
}
// Set payment method
// In CheckoutViewModel
fun setPaymentMethod(paymentId: Int) {
viewModelScope.launch {
try {
// Get the available payment methods, or fetch them if not available
val paymentMethods = _availablePaymentMethods.value ?: run {
val storeId = _checkoutData.value?.sellerId ?: return@launch
val storeResult = repository.fetchStoreDetail(storeId)
if (storeResult is Result.Success && storeResult.data != null) {
val methods = storeResult.data.paymentInfo
_availablePaymentMethods.value = methods
methods
} else {
emptyList()
}
}
// Find the selected payment method
val selectedPayment = paymentMethods.find {
// Change this line to match the actual way you want to identify the payment method
it.name == paymentId.toString() // to do: edit to using it.id
}
if (selectedPayment != null) {
// Set the selected payment
_selectedPayment.value = selectedPayment
// Update the order request with the payment method ID
val currentData = _checkoutData.value ?: return@launch
// Different handling for Buy Now vs Cart checkout
if (currentData.isBuyNow) {
// For Buy Now checkout
val buyRequest = currentData.orderRequest as OrderRequestBuy
val updatedRequest = buyRequest.copy(paymentMethodId = paymentId)
_checkoutData.value = currentData.copy(orderRequest = updatedRequest)
} else {
// For Cart checkout
val cartRequest = currentData.orderRequest as OrderRequest
val updatedRequest = cartRequest.copy(paymentMethodId = paymentId)
_checkoutData.value = currentData.copy(orderRequest = updatedRequest)
}
} else {
// If no matching payment method is found
_errorMessage.value = "Selected payment method not found"
}
} catch (e: Exception) {
_errorMessage.value = "Error setting payment method: ${e.message}"
}
}
}
// Set selected address
fun setSelectedAddress(addressId: Int) {
viewModelScope.launch {
@ -227,39 +287,6 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
}
}
// Set payment method
fun setPaymentMethod(paymentId: Int) {
viewModelScope.launch {
try {
val storeId = _checkoutData.value?.sellerId ?: return@launch
// Use fetchStoreDetail instead of getStore
val storeResult = repository.fetchStoreDetail(storeId)
if (storeResult is Result.Success && storeResult.data != null) {
// Find the selected payment in the payment info list
val payment = storeResult.data.paymentInfo.find { it.name == paymentId.toString() }
_paymentDetails.value = payment
// Update order request if payment isn't null
if (payment != null) {
val currentData = _checkoutData.value ?: return@launch
if (currentData.isBuyNow) {
val buyRequest = currentData.orderRequest as OrderRequestBuy
val updatedRequest = buyRequest.copy(paymentMethodId = paymentId)
_checkoutData.value = currentData.copy(orderRequest = updatedRequest)
} else {
val cartRequest = currentData.orderRequest as OrderRequest
val updatedRequest = cartRequest.copy(paymentMethodId = paymentId)
_checkoutData.value = currentData.copy(orderRequest = updatedRequest)
}
}
}
} catch (e: Exception) {
_errorMessage.value = "Error setting payment method: ${e.message}"
}
}
}
// Create order
fun createOrder() {
viewModelScope.launch {

View File

@ -154,7 +154,7 @@ private fun setupToolbar() {
private fun handleAddressSubmissionState(state: ViewState<String>) {
when (state) {
is ViewState.Loading -> showSubmitLoading(true)
is ViewState.Loading -> null //showSubmitLoading(true)
is ViewState.Success -> {
showSubmitLoading(false)
showSuccessAndFinish(state.data)

View File

@ -4,9 +4,9 @@ import android.content.Intent
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
import com.alya.ecommerce_serang.data.api.retrofit.ApiService
import com.alya.ecommerce_serang.data.repository.OrderRepository
import com.alya.ecommerce_serang.databinding.ActivityAddressBinding
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
@ -14,7 +14,6 @@ import com.alya.ecommerce_serang.utils.SessionManager
class AddressActivity : AppCompatActivity() {
private lateinit var binding: ActivityAddressBinding
private lateinit var apiService: ApiService
private lateinit var sessionManager: SessionManager
private lateinit var adapter: AddressAdapter
@ -26,55 +25,82 @@ class AddressActivity : AppCompatActivity() {
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityAddressBinding.inflate(layoutInflater)
setContentView(binding.root)
sessionManager = SessionManager(this)
apiService = ApiConfig.getApiService(sessionManager)
setupToolbar()
setupRecyclerView()
setupObservers()
adapter = AddressAdapter { selectedId ->
viewModel.selectAddress(selectedId)
viewModel.fetchAddresses()
}
private fun addAddressClicked(){
binding.addAddressClick.setOnClickListener{
val intent = Intent(this, AddAddressActivity::class.java)
startActivity(intent)
}
}
private fun setupToolbar() {
// Remove duplicate toolbar setup
addAddressClicked()
binding.toolbar.setNavigationOnClickListener {
onBackPressedWithResult()
}
}
binding.rvSellerOrder.layoutManager = LinearLayoutManager(this)
binding.rvSellerOrder.adapter = adapter
private fun setupRecyclerView() {
adapter = AddressAdapter { address ->
// Select the address in the ViewModel
viewModel.selectAddress(address.id)
viewModel.fetchAddresses()
// Return immediately with the selected address
returnResultAndFinish(address.id)
}
binding.rvSellerOrder.apply {
layoutManager = LinearLayoutManager(this@AddressActivity)
adapter = this@AddressActivity.adapter
}
}
private fun setupObservers() {
viewModel.addresses.observe(this) { addressList ->
adapter.submitList(addressList)
// Show empty state if needed
// binding.emptyView?.isVisible = addressList.isEmpty()
binding.rvSellerOrder.isVisible = addressList.isNotEmpty()
}
viewModel.selectedAddressId.observe(this) { selectedId ->
adapter.setSelectedAddressId(selectedId)
}
}
private fun setupToolbar() {
binding.toolbar.setNavigationOnClickListener {
private fun onBackPressedWithResult() {
// If an address is selected, return it as result
val selectedId = viewModel.selectedAddressId.value
if (selectedId != null) {
returnResultAndFinish(selectedId)
finish()
} else {
// No selection, just finish
setResult(RESULT_CANCELED)
finish()
}
}
// private fun updateEmptyState(isEmpty: Boolean) {
// binding.layoutEmptyAddresses.isVisible = isEmpty
// binding.rvAddresses.isVisible = !isEmpty
// }
private fun onBackPressedWithResult() {
viewModel.selectedAddressId.value?.let {
val intent = Intent()
intent.putExtra(EXTRA_ADDRESS_ID, it)
setResult(RESULT_OK, intent)
}
finish()
private fun returnResultAndFinish(addressId: Int) {
val intent = Intent()
intent.putExtra(EXTRA_ADDRESS_ID, addressId)
setResult(RESULT_OK, intent)
}
companion object {

View File

@ -13,14 +13,25 @@ import com.alya.ecommerce_serang.data.api.response.profile.AddressesItem
import com.google.android.material.card.MaterialCardView
class AddressAdapter(
private val onAddressClick: (Int) -> Unit
private val onAddressClick: (AddressesItem) -> Unit
) : ListAdapter<AddressesItem, AddressAdapter.AddressViewHolder>(DIFF_CALLBACK) {
private var selectedAddressId: Int? = null
fun setSelectedAddressId(id: Int?) {
val oldSelectedId = selectedAddressId
selectedAddressId = id
notifyDataSetChanged()
// Only refresh the changed items
if (oldSelectedId != null) {
val oldPosition = currentList.indexOfFirst { it.id == oldSelectedId }
if (oldPosition >= 0) notifyItemChanged(oldPosition)
}
if (id != null) {
val newPosition = currentList.indexOfFirst { it.id == id }
if (newPosition >= 0) notifyItemChanged(newPosition)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AddressViewHolder {
@ -33,7 +44,8 @@ class AddressAdapter(
val address = getItem(position)
holder.bind(address, selectedAddressId == address.id)
holder.itemView.setOnClickListener {
onAddressClick(address.id)
// Pass the whole address object to provide more context
onAddressClick(address)
}
}
@ -46,6 +58,12 @@ class AddressAdapter(
tvName.text = address.recipient
tvDetail.text = "${address.street}, ${address.subdistrict}, ${address.phone}"
// Make selection more visible
card.strokeWidth = if (isSelected) 3 else 0
card.strokeColor = if (isSelected)
ContextCompat.getColor(itemView.context, R.color.blue_400)
else 0
card.setCardBackgroundColor(
ContextCompat.getColor(
itemView.context,

View File

@ -24,6 +24,7 @@
app:title="Alamat Pengiriman " />
<TextView
android:id="@+id/add_address_click"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="textEnd"