diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/OrderItemsItem.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/OrderItemsItem.kt new file mode 100644 index 0000000..f3b0849 --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/OrderItemsItem.kt @@ -0,0 +1,36 @@ +package com.alya.ecommerce_serang.data.api.dto + +import com.google.gson.annotations.SerializedName + +data class OrderItemsItem( + + @field:SerializedName("order_item_id") + val orderItemId: Int? = null, + + @field:SerializedName("review_id") + val reviewId: Int? = null, + + @field:SerializedName("quantity") + val quantity: Int? = null, + + @field:SerializedName("price") + val price: Int? = null, + + @field:SerializedName("subtotal") + val subtotal: Int? = null, + + @field:SerializedName("product_image") + val productImage: String? = null, + + @field:SerializedName("product_id") + val productId: Int? = null, + + @field:SerializedName("store_name") + val storeName: String? = null, + + @field:SerializedName("product_price") + val productPrice: Int? = null, + + @field:SerializedName("product_name") + val productName: String? = null +) diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/sells/OrderDetailResponse.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/sells/OrderDetailResponse.kt index 41293f1..745df63 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/sells/OrderDetailResponse.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/sells/OrderDetailResponse.kt @@ -1,5 +1,6 @@ package com.alya.ecommerce_serang.data.api.response.store.sells +import com.alya.ecommerce_serang.data.api.dto.OrderItemsItem import com.google.gson.annotations.SerializedName data class OrderDetailResponse( @@ -17,7 +18,7 @@ data class Orders( val receiptNum: Any? = null, @field:SerializedName("payment_upload_at") - val paymentUploadAt: Any? = null, + val paymentUploadAt: String? = null, @field:SerializedName("latitude") val latitude: Any? = null, @@ -44,10 +45,10 @@ data class Orders( val street: String? = null, @field:SerializedName("cancel_date") - val cancelDate: Any? = null, + val cancelDate: String? = null, @field:SerializedName("payment_evidence") - val paymentEvidence: Any? = null, + val paymentEvidence: String? = null, @field:SerializedName("longitude") val longitude: Any? = null, @@ -71,16 +72,16 @@ data class Orders( val voucherName: Any? = null, @field:SerializedName("payment_status") - val paymentStatus: Any? = null, + val paymentStatus: String? = null, @field:SerializedName("address_id") val addressId: Int? = null, @field:SerializedName("payment_amount") - val paymentAmount: Any? = null, + val paymentAmount: String? = null, @field:SerializedName("cancel_reason") - val cancelReason: Any? = null, + val cancelReason: String? = null, @field:SerializedName("total_amount") val totalAmount: String? = null, @@ -88,6 +89,9 @@ data class Orders( @field:SerializedName("user_id") val userId: Int? = null, + @field:SerializedName("phone") + val phone: String? = null, + @field:SerializedName("province_id") val provinceId: Int? = null, @@ -103,6 +107,9 @@ data class Orders( @field:SerializedName("pay_info_num") val payInfoNum: String? = null, + @field:SerializedName("recipient") + val recipient: String? = null, + @field:SerializedName("shipment_price") val shipmentPrice: String? = null, @@ -126,4 +133,4 @@ data class Orders( @field:SerializedName("city_id") val cityId: Int? = null -) +) \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/sells/OrderListResponse.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/sells/OrderListResponse.kt index 8c58937..5439b14 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/sells/OrderListResponse.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/sells/OrderListResponse.kt @@ -1,5 +1,6 @@ package com.alya.ecommerce_serang.data.api.response.store.sells +import com.alya.ecommerce_serang.data.api.dto.OrderItemsItem import com.google.gson.annotations.SerializedName data class OrderListResponse( @@ -11,39 +12,6 @@ data class OrderListResponse( val message: String? = null ) -data class OrderItemsItem( - - @field:SerializedName("order_item_id") - val orderItemId: Int? = null, - - @field:SerializedName("review_id") - val reviewId: Any? = null, - - @field:SerializedName("quantity") - val quantity: Int? = null, - - @field:SerializedName("price") - val price: Int? = null, - - @field:SerializedName("subtotal") - val subtotal: Int? = null, - - @field:SerializedName("product_image") - val productImage: String? = null, - - @field:SerializedName("product_id") - val productId: Int? = null, - - @field:SerializedName("store_name") - val storeName: String? = null, - - @field:SerializedName("product_price") - val productPrice: Int? = null, - - @field:SerializedName("product_name") - val productName: String? = null -) - data class OrdersItem( @field:SerializedName("receipt_num") diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt index 8ef69e7..7265b5f 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt @@ -327,6 +327,11 @@ interface ApiService { @Path("status") status: String ): Response + @GET("order/detail/{id}") + suspend fun getSellDetail( + @Path("id") orderId: Int + ): Response + @PUT("store/order/update") suspend fun confirmOrder( @Body confirmOrder : CompletedOrderRequest diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/repository/SellsRepository.kt b/app/src/main/java/com/alya/ecommerce_serang/data/repository/SellsRepository.kt index 076e6e5..2b9df12 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/data/repository/SellsRepository.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/data/repository/SellsRepository.kt @@ -2,6 +2,7 @@ package com.alya.ecommerce_serang.data.repository import android.util.Log import com.alya.ecommerce_serang.data.api.dto.PaymentConfirmRequest +import com.alya.ecommerce_serang.data.api.response.store.sells.OrderDetailResponse import com.alya.ecommerce_serang.data.api.response.store.sells.OrderListResponse import com.alya.ecommerce_serang.data.api.response.store.sells.PaymentConfirmationResponse import com.alya.ecommerce_serang.data.api.retrofit.ApiService @@ -32,6 +33,16 @@ class SellsRepository(private val apiService: ApiService) { } } + suspend fun getSellDetails(orderId: Int): OrderDetailResponse? { + return try { + val response = apiService.getSellDetail(orderId) + if (response.isSuccessful) response.body() else null + } catch (e: Exception) { + Log.e("SellsRepository", "Error getting order details", e) + null + } + } + suspend fun updateOrderStatus(orderId: Int?, status: String) { try { val response = apiService.updateOrder(orderId, status) diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/DetailSellsActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/DetailSellsActivity.kt index 87cdd04..1a6cf1f 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/DetailSellsActivity.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/DetailSellsActivity.kt @@ -2,15 +2,20 @@ package com.alya.ecommerce_serang.ui.profile.mystore.sells import android.os.Bundle import android.util.Log +import android.widget.Toast import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.LinearLayoutManager import com.alya.ecommerce_serang.data.api.response.store.sells.Orders +import com.alya.ecommerce_serang.data.api.response.store.sells.OrdersItem import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig +import com.alya.ecommerce_serang.data.repository.AddressRepository import com.alya.ecommerce_serang.data.repository.SellsRepository import com.alya.ecommerce_serang.databinding.ActivityDetailSellsBinding +import com.alya.ecommerce_serang.ui.profile.mystore.sells.shipment.DetailShipmentActivity import com.alya.ecommerce_serang.utils.BaseViewModelFactory import com.alya.ecommerce_serang.utils.SessionManager +import com.alya.ecommerce_serang.utils.viewmodel.AddressViewModel import com.alya.ecommerce_serang.utils.viewmodel.SellsViewModel import com.google.gson.Gson import java.text.SimpleDateFormat @@ -28,6 +33,7 @@ class DetailSellsActivity : AppCompatActivity() { private val viewModel: SellsViewModel by viewModels { BaseViewModelFactory { + sessionManager = SessionManager(this) val apiService = ApiConfig.getApiService(sessionManager) val sellsRepository = SellsRepository(apiService) SellsViewModel(sellsRepository) @@ -51,48 +57,88 @@ class DetailSellsActivity : AppCompatActivity() { layoutManager = LinearLayoutManager(this@DetailSellsActivity) } - val sellsJson = intent.getStringExtra("sells") + val sellsJson = intent.getStringExtra("sells_data") if (sellsJson != null) { try { - sells = Gson().fromJson(sellsJson, Orders::class.java) - showOrderDetails() + val basicOrder = Gson().fromJson(sellsJson, OrdersItem::class.java) + basicOrder.orderId.let { + viewModel.getSellDetails(it) + } } catch (e: Exception) { Log.e("DetailSellsActivity", "Failed to parse order data", e) } } else { - Log.e("DetailSellsActivity", "Order data is missing") + Log.e("DetailSellsActivity", "No order passed in intent") + } + + observeOrderDetails() + } + + private fun observeOrderDetails() { + viewModel.sellDetails.observe(this) { order -> + if (order != null) { + sells = order + showOrderDetails() + } else { + Log.e("DetailSellsActivity", "❌ Failed to retrieve order details") + } } } - private fun showOrderDetails() { + private fun showOrderDetails() = with(binding) { sells?.let { sell -> when (sell.orderStatus) { - "pending", "unpaid" -> { - binding.tvOrderSellsTitle.text = "Pesanan Belum Dibayar" - } - "shipped" -> { - binding.tvOrderSellsTitle.text = "Pesanan Telah Dikirim" - } - "delivered" -> { - binding.tvOrderSellsTitle.text = "Pesanan Telah Sampai" - } - "completed" -> { - binding.tvOrderSellsTitle.text = "Pesanan Selesai" - } - "canceled" -> { - binding.tvOrderSellsTitle.text = "Pesanan Dibatalkan" - } + "pending", "unpaid" -> tvOrderSellsTitle.text = "Pesanan Belum Dibayar" + "shipped" -> tvOrderSellsTitle.text = "Pesanan Telah Dikirim" + "delivered" -> tvOrderSellsTitle.text = "Pesanan Telah Sampai" + "completed" -> tvOrderSellsTitle.text = "Pesanan Selesai" + "canceled" -> tvOrderSellsTitle.text = "Pesanan Dibatalkan" + else -> tvOrderSellsTitle.text = "Status Tidak Diketahui" } - binding.tvOrderNumber.text = sell.orderId.toString() - binding.tvOrderCustomer.text = sell.username - binding.tvOrderDate.text = formatDate(sell.updatedAt.toString()) - binding.tvOrderTotalProduct.text = "(${sell.orderItems?.size} Barang)" - binding.tvOrderSubtotal.text = formatPrice(sell.totalAmount.toString()) - binding.tvOrderShipPrice.text = formatPrice(sell.shipmentPrice.toString()) - binding.tvOrderPrice.text = formatPrice(sell.totalAmount.toString()) - binding.tvOrderRecipient.text = sell.username - binding.tvOrderRecipientNum.text = sell.receiptNum.toString() + tvOrderNumber.text = sell.orderId.toString() + tvOrderCustomer.text = sell.username + tvOrderDate.text = formatDate(sell.updatedAt.toString()) + tvOrderTotalProduct.text = "(${sell.orderItems?.size ?: 0} Barang)" + tvOrderSubtotal.text = formatPrice(sell.totalAmount.toString()) + tvOrderShipPrice.text = formatPrice(sell.shipmentPrice.toString()) + tvOrderPrice.text = formatPrice(sell.totalAmount.toString()) + tvOrderRecipient.text = sell.recipient ?: "-" + tvOrderRecipientNum.text = sell.receiptNum?.toString() ?: "-" + + val cityId = sell.cityId?.toString() + val provinceId = sell.provinceId?.toString() + + if (cityId != null && provinceId != null) { + val viewModelAddress: AddressViewModel by viewModels { + BaseViewModelFactory { + val apiService = ApiConfig.getApiService(sessionManager) + val addressRepository = AddressRepository(apiService) + AddressViewModel(addressRepository) + } + } + + viewModelAddress.fetchCities(provinceId) + viewModelAddress.fetchProvinces() + + viewModelAddress.cities.observe(this@DetailSellsActivity) { cities -> + val cityName = cities.find { it.cityId == cityId }?.cityName + viewModelAddress.provinces.observe(this@DetailSellsActivity) { provinces -> + val provinceName = provinces.find { it.provinceId == provinceId }?.provinceName + + val fullAddress = listOfNotNull( + sell.street, + sell.subdistrict, + cityName, + provinceName + ).joinToString(", ") + + tvOrderRecipientAddress.text = fullAddress + } + } + } else { + tvOrderRecipientAddress.text = "-" + } sell.orderItems?.let { productAdapter.submitList(it.filterNotNull()) @@ -106,13 +152,11 @@ class DetailSellsActivity : AppCompatActivity() { inputFormat.timeZone = TimeZone.getTimeZone("UTC") val outputFormat = SimpleDateFormat("dd MMM yyyy, HH.mm", Locale("id", "ID")) - val date = inputFormat.parse(dateString) date?.let { val calendar = Calendar.getInstance() calendar.time = it - outputFormat.format(calendar.time) } ?: dateString } catch (e: Exception) { @@ -123,7 +167,6 @@ class DetailSellsActivity : AppCompatActivity() { private fun formatPrice(price: String): String { val priceDouble = price.toDoubleOrNull() ?: 0.0 - val formattedPrice = String.format(Locale("id", "ID"), "Rp%,.0f", priceDouble) - return formattedPrice + return String.format(Locale("id", "ID"), "Rp%,.0f", priceDouble) } } \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsProductAdapter.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsProductAdapter.kt index 47caa84..1ad828c 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsProductAdapter.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsProductAdapter.kt @@ -6,8 +6,9 @@ import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView import androidx.recyclerview.widget.RecyclerView +import com.alya.ecommerce_serang.BuildConfig.BASE_URL import com.alya.ecommerce_serang.R -import com.alya.ecommerce_serang.data.api.response.store.sells.OrderItemsItem +import com.alya.ecommerce_serang.data.api.dto.OrderItemsItem import com.bumptech.glide.Glide import java.util.Locale @@ -33,9 +34,18 @@ class SellsProductAdapter : RecyclerView.Adapter { + if (img.startsWith("/")) BASE_URL + img.substring(1) else img + } + else -> R.drawable.placeholder_image + } + Glide.with(ivProduct.context) - .load(item.productImage) + .load(fullImageUrl) .placeholder(R.drawable.placeholder_image) + .error(R.drawable.placeholder_image) .into(ivProduct) } } diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/payment/DetailPaymentActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/payment/DetailPaymentActivity.kt index 25b44a4..2673b32 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/payment/DetailPaymentActivity.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/payment/DetailPaymentActivity.kt @@ -5,6 +5,7 @@ import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.graphics.drawable.Drawable import android.os.Bundle +import android.util.Log import android.view.View import android.view.ViewGroup import android.view.Window @@ -18,32 +19,38 @@ import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.LinearLayoutManager import com.alya.ecommerce_serang.BuildConfig.BASE_URL import com.alya.ecommerce_serang.R +import com.alya.ecommerce_serang.data.api.response.store.sells.Orders import com.alya.ecommerce_serang.data.api.response.store.sells.OrdersItem import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig +import com.alya.ecommerce_serang.data.repository.AddressRepository import com.alya.ecommerce_serang.data.repository.SellsRepository import com.alya.ecommerce_serang.databinding.ActivityDetailPaymentBinding import com.alya.ecommerce_serang.ui.profile.mystore.sells.SellsProductAdapter +import com.alya.ecommerce_serang.ui.profile.mystore.sells.shipment.DetailShipmentActivity import com.alya.ecommerce_serang.utils.BaseViewModelFactory import com.alya.ecommerce_serang.utils.SessionManager +import com.alya.ecommerce_serang.utils.viewmodel.AddressViewModel import com.alya.ecommerce_serang.utils.viewmodel.SellsViewModel import com.bumptech.glide.Glide import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition import com.google.gson.Gson import java.text.SimpleDateFormat +import java.util.Calendar import java.util.Locale import java.util.TimeZone class DetailPaymentActivity : AppCompatActivity() { private lateinit var binding: ActivityDetailPaymentBinding - private lateinit var sells: OrdersItem + private var sells: Orders? = null private lateinit var productAdapter: SellsProductAdapter private lateinit var sessionManager: SessionManager - private val viewModel: SellsViewModel by viewModels { BaseViewModelFactory { + sessionManager = SessionManager(this) val apiService = ApiConfig.getApiService(sessionManager) val sellsRepository = SellsRepository(apiService) SellsViewModel(sellsRepository) @@ -55,59 +62,120 @@ class DetailPaymentActivity : AppCompatActivity() { binding = ActivityDetailPaymentBinding.inflate(layoutInflater) setContentView(binding.root) - sessionManager = SessionManager(this) - - val orderJson = intent.getStringExtra("sells_data") - sells = Gson().fromJson(orderJson, OrdersItem::class.java) - + binding.header.headerTitle.text = "Detail Pesanan" binding.header.headerLeftIcon.setOnClickListener { -// onBackPressed() + onBackPressed() finish() } - setupRecyclerView() - bindOrderDetails() - setupPaymentEvidenceViewer() + val sellsJson = intent.getStringExtra("sells_data") + if (sellsJson != null) { + try { + val basicOrder = Gson().fromJson(sellsJson, OrdersItem::class.java) + basicOrder.orderId.let { + viewModel.getSellDetails(it) + } + } catch (e: Exception) { + Log.e("DetailSellsActivity", "Failed to parse order data", e) + } + } else { + Log.e("DetailSellsActivity", "No order passed in intent") + } + + observeOrderDetails() } - private fun bindOrderDetails() = with(binding) { - tvOrderNumber.text = sells.orderId.toString() - tvOrderCustomer.text = sells.username - tvOrderDate.text = formatDate(sells.createdAt) - tvOrderDue.text = formatDate(sells.updatedAt) - - tvOrderTotalProduct.text = "(${sells.orderItems?.size ?: 0} Barang)" - tvOrderSubtotal.text = "Rp${sells.totalAmount}" - tvOrderPrice.text = "Rp${sells.totalAmount}" - tvOrderShipPrice.text = "Rp${sells.shipmentPrice}" - - tvOrderRecipient.text = sells.username -// tvOrderRecipientNum.text = sells.phone - tvOrderRecipientAddress.text = sells.street -// sells.paymentEvidence - - binding.btnConfirmPayment.setOnClickListener{ - viewModel.confirmPayment(sells.orderId, "confirmed") - finish() + private fun observeOrderDetails() { + viewModel.sellDetails.observe(this) { order -> + if (order != null) { + sells = order + showOrderDetails() + } else { + Log.e("DetailShipmentActivity", "❌ Failed to retrieve order details") + } } } - private fun setupRecyclerView() { - productAdapter = SellsProductAdapter() + private fun showOrderDetails() = with(binding) { + sells?.let { sell -> + tvOrderNumber.text = sell.orderId.toString() + tvOrderCustomer.text = sell.username + tvOrderDate.text = formatDate(sell.updatedAt.toString()) + tvOrderTotalProduct.text = "(${sell.orderItems?.size ?: 0} Barang)" + tvOrderSubtotal.text = formatPrice(sell.totalAmount.toString()) + tvOrderShipPrice.text = formatPrice(sell.shipmentPrice.toString()) + tvOrderPrice.text = formatPrice(sell.totalAmount.toString()) + tvOrderRecipient.text = sell.recipient ?: "-" + tvOrderRecipientNum.text = sell.receiptNum?.toString() ?: "-" - binding.rvProductItems.apply { - layoutManager = LinearLayoutManager(this@DetailPaymentActivity) - adapter = productAdapter + val cityId = sell.cityId?.toString() + val provinceId = sell.provinceId?.toString() + + if (cityId != null && provinceId != null) { + val viewModelAddress: AddressViewModel by viewModels { + BaseViewModelFactory { + val apiService = ApiConfig.getApiService(sessionManager) + val addressRepository = AddressRepository(apiService) + AddressViewModel(addressRepository) + } + } + + viewModelAddress.fetchCities(provinceId) + viewModelAddress.fetchProvinces() + + viewModelAddress.cities.observe(this@DetailPaymentActivity) { cities -> + val cityName = cities.find { it.cityId == cityId }?.cityName + viewModelAddress.provinces.observe(this@DetailPaymentActivity) { provinces -> + val provinceName = provinces.find { it.provinceId == provinceId }?.provinceName + + val fullAddress = listOfNotNull( + sell.street, + sell.subdistrict, + cityName, + provinceName + ).joinToString(", ") + + tvOrderRecipientAddress.text = fullAddress + } + } + } else { + tvOrderRecipientAddress.text = "-" + } + + sell.orderItems?.let { + productAdapter.submitList(it.filterNotNull()) + } } + } - // Submit the order items to the adapter - productAdapter.submitList(sells.orderItems ?: emptyList()) + private fun formatDate(dateString: String): String { + return try { + val inputFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault()) + inputFormat.timeZone = TimeZone.getTimeZone("UTC") + + val outputFormat = SimpleDateFormat("dd MMM yyyy, HH.mm", Locale("id", "ID")) + val date = inputFormat.parse(dateString) + + date?.let { + val calendar = Calendar.getInstance() + calendar.time = it + outputFormat.format(calendar.time) + } ?: dateString + } catch (e: Exception) { + Log.e("DateFormatting", "Error formatting date: ${e.message}") + dateString + } + } + + private fun formatPrice(price: String): String { + val priceDouble = price.toDoubleOrNull() ?: 0.0 + return String.format(Locale("id", "ID"), "Rp%,.0f", priceDouble) } private fun setupPaymentEvidenceViewer() { binding.tvOrderSellsDesc.setOnClickListener { - val paymentEvidence = sells.paymentEvidence + val paymentEvidence = sells?.paymentEvidence if (!paymentEvidence.isNullOrEmpty()) { showPaymentEvidenceDialog(paymentEvidence) } else { @@ -154,7 +222,7 @@ class DetailPaymentActivity : AppCompatActivity() { .error(R.drawable.placeholder_image) .diskCacheStrategy(DiskCacheStrategy.ALL) .into(object : CustomTarget() { - override fun onResourceReady(resource: Drawable, transition: com.bumptech.glide.request.transition.Transition?) { + override fun onResourceReady(resource: Drawable, transition: Transition?) { progressBar.visibility = View.GONE imageView.setImageDrawable(resource) } @@ -184,17 +252,4 @@ class DetailPaymentActivity : AppCompatActivity() { dialog.show() } - - - private fun formatDate(dateStr: String?): String { - return try { - val inputFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault()) - inputFormat.timeZone = TimeZone.getTimeZone("UTC") - val outputFormat = SimpleDateFormat("dd MMM yyyy, HH:mm", Locale("id", "ID")) - val date = inputFormat.parse(dateStr ?: "") - outputFormat.format(date!!) - } catch (e: Exception) { - "-" - } - } } \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipment/DetailShipmentActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipment/DetailShipmentActivity.kt index 07518cd..fd5329d 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipment/DetailShipmentActivity.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipment/DetailShipmentActivity.kt @@ -1,71 +1,155 @@ package com.alya.ecommerce_serang.ui.profile.mystore.sells.shipment import android.os.Bundle +import android.util.Log +import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity -import androidx.recyclerview.widget.LinearLayoutManager +import com.alya.ecommerce_serang.data.api.response.store.sells.Orders import com.alya.ecommerce_serang.data.api.response.store.sells.OrdersItem +import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig +import com.alya.ecommerce_serang.data.repository.AddressRepository +import com.alya.ecommerce_serang.data.repository.SellsRepository import com.alya.ecommerce_serang.databinding.ActivityDetailShipmentBinding import com.alya.ecommerce_serang.ui.profile.mystore.sells.SellsProductAdapter +import com.alya.ecommerce_serang.utils.BaseViewModelFactory +import com.alya.ecommerce_serang.utils.SessionManager +import com.alya.ecommerce_serang.utils.viewmodel.AddressViewModel +import com.alya.ecommerce_serang.utils.viewmodel.SellsViewModel import com.google.gson.Gson import java.text.SimpleDateFormat +import java.util.Calendar import java.util.Locale import java.util.TimeZone +import kotlin.getValue class DetailShipmentActivity : AppCompatActivity() { private lateinit var binding: ActivityDetailShipmentBinding - private lateinit var sells: OrdersItem + private lateinit var sessionManager: SessionManager + private var sells: Orders? = null private lateinit var productAdapter: SellsProductAdapter + private val viewModel: SellsViewModel by viewModels { + BaseViewModelFactory { + sessionManager = SessionManager(this) + val apiService = ApiConfig.getApiService(sessionManager) + val sellsRepository = SellsRepository(apiService) + SellsViewModel(sellsRepository) + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityDetailShipmentBinding.inflate(layoutInflater) setContentView(binding.root) - val orderJson = intent.getStringExtra("sells_data") - sells = Gson().fromJson(orderJson, OrdersItem::class.java) - - setupRecyclerView() - bindOrderDetails() - } - - private fun bindOrderDetails() = with(binding) { - tvOrderNumber.text = sells.orderId.toString() - tvOrderCustomer.text = sells.username - tvOrderDate.text = formatDate(sells.createdAt) - tvOrderDue.text = formatDate(sells.updatedAt) - - tvOrderTotalProduct.text = "(${sells.orderItems?.size ?: 0} Barang)" - tvOrderSubtotal.text = "Rp${sells.totalAmount}" - tvOrderPrice.text = "Rp${sells.totalAmount}" - tvOrderShipPrice.text = "Rp${sells.shipmentPrice}" - - tvOrderRecipient.text = sells.username -// tvOrderRecipientNum.text = sells.phone - tvOrderRecipientAddress.text = sells.street - } - - private fun setupRecyclerView() { - productAdapter = SellsProductAdapter() - - binding.rvProductItems.apply { - layoutManager = LinearLayoutManager(this@DetailShipmentActivity) - adapter = productAdapter + binding.header.headerTitle.text = "Detail Pesanan" + binding.header.headerLeftIcon.setOnClickListener { + onBackPressed() + finish() } - // Submit the order items to the adapter - productAdapter.submitList(sells.orderItems ?: emptyList()) + val sellsJson = intent.getStringExtra("sells_data") + if (sellsJson != null) { + try { + val basicOrder = Gson().fromJson(sellsJson, OrdersItem::class.java) + basicOrder.orderId.let { + viewModel.getSellDetails(it) + } + } catch (e: Exception) { + Log.e("DetailSellsActivity", "Failed to parse order data", e) + } + } else { + Log.e("DetailSellsActivity", "No order passed in intent") + } + + observeOrderDetails() } - private fun formatDate(dateStr: String?): String { + private fun observeOrderDetails() { + viewModel.sellDetails.observe(this) { order -> + if (order != null) { + sells = order + showOrderDetails() + } else { + Log.e("DetailShipmentActivity", "❌ Failed to retrieve order details") + } + } + } + + private fun showOrderDetails() = with(binding) { + sells?.let { sell -> + tvOrderNumber.text = sell.orderId.toString() + tvOrderCustomer.text = sell.username + tvOrderDate.text = formatDate(sell.updatedAt.toString()) + tvOrderTotalProduct.text = "(${sell.orderItems?.size ?: 0} Barang)" + tvOrderSubtotal.text = formatPrice(sell.totalAmount.toString()) + tvOrderShipPrice.text = formatPrice(sell.shipmentPrice.toString()) + tvOrderPrice.text = formatPrice(sell.totalAmount.toString()) + tvOrderRecipient.text = sell.recipient ?: "-" + tvOrderRecipientNum.text = sell.receiptNum?.toString() ?: "-" + + val cityId = sell.cityId?.toString() + val provinceId = sell.provinceId?.toString() + + if (cityId != null && provinceId != null) { + val viewModelAddress: AddressViewModel by viewModels { + BaseViewModelFactory { + val apiService = ApiConfig.getApiService(sessionManager) + val addressRepository = AddressRepository(apiService) + AddressViewModel(addressRepository) + } + } + + viewModelAddress.fetchCities(provinceId) + viewModelAddress.fetchProvinces() + + viewModelAddress.cities.observe(this@DetailShipmentActivity) { cities -> + val cityName = cities.find { it.cityId == cityId }?.cityName + viewModelAddress.provinces.observe(this@DetailShipmentActivity) { provinces -> + val provinceName = provinces.find { it.provinceId == provinceId }?.provinceName + + val fullAddress = listOfNotNull( + sell.street, + sell.subdistrict, + cityName, + provinceName + ).joinToString(", ") + + tvOrderRecipientAddress.text = fullAddress + } + } + } else { + tvOrderRecipientAddress.text = "-" + } + + sell.orderItems?.let { + productAdapter.submitList(it.filterNotNull()) + } + } + } + + private fun formatDate(dateString: String): String { return try { val inputFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault()) inputFormat.timeZone = TimeZone.getTimeZone("UTC") - val outputFormat = SimpleDateFormat("dd MMM yyyy, HH:mm", Locale("id", "ID")) - val date = inputFormat.parse(dateStr ?: "") - outputFormat.format(date!!) + + val outputFormat = SimpleDateFormat("dd MMM yyyy, HH.mm", Locale("id", "ID")) + val date = inputFormat.parse(dateString) + + date?.let { + val calendar = Calendar.getInstance() + calendar.time = it + outputFormat.format(calendar.time) + } ?: dateString } catch (e: Exception) { - "-" + Log.e("DateFormatting", "Error formatting date: ${e.message}") + dateString } } + + private fun formatPrice(price: String): String { + val priceDouble = price.toDoubleOrNull() ?: 0.0 + return String.format(Locale("id", "ID"), "Rp%,.0f", priceDouble) + } } \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/SellsViewModel.kt b/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/SellsViewModel.kt index e0c8321..101ff93 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/SellsViewModel.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/SellsViewModel.kt @@ -5,12 +5,15 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.alya.ecommerce_serang.data.api.dto.OrderItemsItem import com.alya.ecommerce_serang.data.api.dto.PaymentConfirmRequest +import com.alya.ecommerce_serang.data.api.response.store.sells.Orders import com.alya.ecommerce_serang.data.api.response.store.sells.OrdersItem import com.alya.ecommerce_serang.data.api.response.store.sells.PaymentConfirmationResponse import com.alya.ecommerce_serang.data.repository.Result import com.alya.ecommerce_serang.data.repository.SellsRepository import com.alya.ecommerce_serang.ui.order.address.ViewState +import com.alya.ecommerce_serang.ui.order.history.HistoryViewModel import kotlinx.coroutines.launch class SellsViewModel(private val repository: SellsRepository) : ViewModel() { @@ -22,9 +25,28 @@ class SellsViewModel(private val repository: SellsRepository) : ViewModel() { private val _sells = MutableLiveData>>() val sells: LiveData>> = _sells + private val _sellDetails = MutableLiveData() + val sellDetails: LiveData get() = _sellDetails + private val _confirmPaymentStore = MutableLiveData>() val confirmPaymentStore: LiveData> = _confirmPaymentStore + // LiveData untuk OrderItems + private val _orderItems = MutableLiveData?>() + val orderItems: LiveData?> get() = _orderItems + + private val _isLoading = MutableLiveData() + val isLoading: LiveData = _isLoading + + private val _message = MutableLiveData() + val message: LiveData = _message + + private val _isSuccess = MutableLiveData() + val isSuccess: LiveData = _isSuccess + + private val _error = MutableLiveData() + val error: LiveData get() = _error + fun getSellList(status: String) { Log.d(TAG, "========== Starting getSellList ==========") Log.d(TAG, "Requested status: '$status'") @@ -48,15 +70,15 @@ class SellsViewModel(private val repository: SellsRepository) : ViewModel() { // Log the entire result data structure Log.d(TAG, "Raw result data: ${result.data}") - Log.d(TAG, "Result data class: ${result.data?.javaClass?.simpleName}") + Log.d(TAG, "Result data class: ${result.data.javaClass.simpleName}") val orders = result.data.orders Log.d(TAG, "Extracted orders list: $orders") - Log.d(TAG, "Orders list class: ${orders?.javaClass?.simpleName}") - Log.d(TAG, "Orders count: ${orders?.size ?: 0}") + Log.d(TAG, "Orders list class: ${orders.javaClass.simpleName}") + Log.d(TAG, "Orders count: ${orders.size}") // Check if orders list is null or empty - if (orders == null) { + if (false) { Log.w(TAG, "⚠️ Orders list is NULL") } else if (orders.isEmpty()) { Log.w(TAG, "⚠️ Orders list is EMPTY") @@ -67,17 +89,17 @@ class SellsViewModel(private val repository: SellsRepository) : ViewModel() { orders.forEachIndexed { index, order -> Log.d(TAG, "--- Order ${index + 1}/${orders.size} ---") Log.d(TAG, " Order object: $order") - Log.d(TAG, " Order class: ${order?.javaClass?.simpleName}") - Log.d(TAG, " - ID: ${order?.orderId}") - Log.d(TAG, " - Status: '${order?.status}'") - Log.d(TAG, " - Customer: '${order?.username}'") - Log.d(TAG, " - Total: ${order?.totalAmount}") - Log.d(TAG, " - Items count: ${order?.orderItems?.size ?: 0}") - Log.d(TAG, " - Created at: ${order?.createdAt}") - Log.d(TAG, " - Updated at: ${order?.updatedAt}") + Log.d(TAG, " Order class: ${order.javaClass.simpleName}") + Log.d(TAG, " - ID: ${order.orderId}") + Log.d(TAG, " - Status: '${order.status}'") + Log.d(TAG, " - Customer: '${order.username}'") + Log.d(TAG, " - Total: ${order.totalAmount}") + Log.d(TAG, " - Items count: ${order.orderItems?.size ?: 0}") + Log.d(TAG, " - Created at: ${order.createdAt}") + Log.d(TAG, " - Updated at: ${order.updatedAt}") // Log order items if available - order?.orderItems?.let { items -> + order.orderItems?.let { items -> Log.d(TAG, " Order items:") items.forEachIndexed { itemIndex, item -> Log.d(TAG, " Item ${itemIndex + 1}: ${item?.productName} (Qty: ${item?.quantity})") @@ -87,8 +109,8 @@ class SellsViewModel(private val repository: SellsRepository) : ViewModel() { } // Set the ViewState to Success - _sells.value = ViewState.Success(orders ?: emptyList()) - Log.d(TAG, "✅ ViewState.Success set with ${orders?.size ?: 0} orders") + _sells.value = ViewState.Success(orders) + Log.d(TAG, "✅ ViewState.Success set with ${orders.size} orders") } is Result.Error -> { @@ -125,6 +147,28 @@ class SellsViewModel(private val repository: SellsRepository) : ViewModel() { Log.d(TAG, "========== getSellList method completed ==========") } + fun getSellDetails(orderId: Int) { + Log.d(TAG, "========== Starting getSellDetails ==========") + Log.d(TAG, "Fetching details for order ID: $orderId") + _isLoading.value = true + viewModelScope.launch { + try { + val response = repository.getSellDetails(orderId) + if (response != null) { + _sellDetails.value = response.orders + _orderItems.value = response.orders?.orderItems?.filterNotNull() + } else { + _error.value = "Gagal memuat detail pesanan" + } + } catch (e: Exception) { + _error.value = "Terjadi kesalahan: ${e.message}" + Log.e(SellsViewModel.Companion.TAG, "Error fetching order details", e) + } finally { + _isLoading.value = false + } + } + } + fun updateOrderStatus(orderId: Int?, status: String) { Log.d(TAG, "========== Starting updateOrderStatus ==========") Log.d(TAG, "Updating order status: orderId=$orderId, status='$status'") diff --git a/app/src/main/res/layout/activity_detail_sells.xml b/app/src/main/res/layout/activity_detail_sells.xml index 2b5e0da..8a1e70c 100644 --- a/app/src/main/res/layout/activity_detail_sells.xml +++ b/app/src/main/res/layout/activity_detail_sells.xml @@ -46,8 +46,7 @@ android:layout_marginStart="12dp" app:layout_constraintStart_toStartOf="@id/shape_payment_order_title" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="parent" - android:gravity="center"> + app:layout_constraintTop_toTopOf="parent"> + android:layout_marginEnd="16dp" + android:visibility="gone"/> @@ -377,19 +377,4 @@ - - -