mirror of
https://github.com/shaulascr/ecommerce_serang.git
synced 2025-08-10 09:22:21 +00:00
update detail order
This commit is contained in:
@ -29,6 +29,9 @@
|
||||
android:theme="@style/Theme.Ecommerce_serang"
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".ui.order.history.detailorder.DetailOrderStatusActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.order.review.CreateReviewActivity"
|
||||
android:exported="false" />
|
||||
|
@ -7,9 +7,13 @@ import android.util.Log
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.alya.ecommerce_serang.data.api.dto.CheckoutData
|
||||
@ -47,6 +51,22 @@ class CheckoutActivity : AppCompatActivity() {
|
||||
|
||||
sessionManager = SessionManager(this)
|
||||
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
enableEdgeToEdge()
|
||||
|
||||
// Apply insets to your root layout
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view, windowInsets ->
|
||||
val systemBars = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
view.setPadding(
|
||||
systemBars.left,
|
||||
systemBars.top,
|
||||
systemBars.right,
|
||||
systemBars.bottom
|
||||
)
|
||||
windowInsets
|
||||
}
|
||||
|
||||
// Setup UI components
|
||||
setupToolbar()
|
||||
setupObservers()
|
||||
|
@ -329,7 +329,6 @@ class OrderHistoryAdapter(
|
||||
|
||||
private fun formatShipmentDate(dateString: String, estimate: String): String {
|
||||
return try {
|
||||
// Parse the input date
|
||||
val estimateTD = if (estimate.isNullOrEmpty()) 0 else estimate.toInt()
|
||||
|
||||
val inputFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault())
|
||||
|
@ -1,10 +1,13 @@
|
||||
package com.alya.ecommerce_serang.ui.order.history
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
@ -14,6 +17,7 @@ import com.alya.ecommerce_serang.data.repository.OrderRepository
|
||||
import com.alya.ecommerce_serang.data.repository.Result
|
||||
import com.alya.ecommerce_serang.databinding.FragmentOrderListBinding
|
||||
import com.alya.ecommerce_serang.ui.order.address.ViewState
|
||||
import com.alya.ecommerce_serang.ui.order.history.detailorder.DetailOrderStatusActivity
|
||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||
import com.alya.ecommerce_serang.utils.SessionManager
|
||||
|
||||
@ -121,10 +125,21 @@ class OrderListFragment : Fragment() {
|
||||
viewModel.getOrderList(status)
|
||||
}
|
||||
|
||||
private val detailOrderLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult()
|
||||
) { result ->
|
||||
if (result.resultCode == Activity.RESULT_OK) {
|
||||
// Refresh order list when returning with OK result
|
||||
viewModel.getOrderList(status)
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigateToOrderDetail(order: OrdersItem) {
|
||||
// In a real app, you would navigate to order detail screen
|
||||
// For example: findNavController().navigate(OrderListFragmentDirections.actionToOrderDetail(order.orderId))
|
||||
Toast.makeText(requireContext(), "Order ID: ${order.orderId}", Toast.LENGTH_SHORT).show()
|
||||
val intent = Intent(requireContext(), DetailOrderStatusActivity::class.java).apply {
|
||||
putExtra("ORDER_ID", order.orderId)
|
||||
putExtra("ORDER_STATUS", status) // Pass the current status
|
||||
}
|
||||
detailOrderLauncher.launch(intent)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
@ -148,4 +163,5 @@ class OrderListFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package com.alya.ecommerce_serang.ui.order.history.detailorder
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.alya.ecommerce_serang.R
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.OrderListItemsItem
|
||||
import com.bumptech.glide.Glide
|
||||
|
||||
class DetailOrderItemsAdapter : RecyclerView.Adapter<DetailOrderItemsAdapter.DetailOrderItemViewHolder>() {
|
||||
|
||||
private val items = mutableListOf<OrderListItemsItem>()
|
||||
|
||||
fun submitList(newItems: List<OrderListItemsItem>) {
|
||||
items.clear()
|
||||
items.addAll(newItems)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DetailOrderItemViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.item_order_detail_product, parent, false)
|
||||
return DetailOrderItemViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: DetailOrderItemViewHolder, position: Int) {
|
||||
holder.bind(items[position])
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = items.size
|
||||
|
||||
inner class DetailOrderItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
private val ivProduct: ImageView = itemView.findViewById(R.id.ivProduct)
|
||||
private val tvProductName: TextView = itemView.findViewById(R.id.tvProductName)
|
||||
private val tvQuantity: TextView = itemView.findViewById(R.id.tvQuantity)
|
||||
private val tvPrice: TextView = itemView.findViewById(R.id.tvPrice)
|
||||
|
||||
fun bind(item: OrderListItemsItem) {
|
||||
// Load product image
|
||||
Glide.with(itemView.context)
|
||||
.load(item.productImage)
|
||||
.placeholder(R.drawable.placeholder_image)
|
||||
.error(R.drawable.placeholder_image)
|
||||
.into(ivProduct)
|
||||
|
||||
tvProductName.text = item.productName
|
||||
tvQuantity.text = "${item.quantity} buah"
|
||||
tvPrice.text = "Rp${item.price}"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,683 @@
|
||||
package com.alya.ecommerce_serang.ui.order.history.detailorder
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.Window
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.AutoCompleteTextView
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.alya.ecommerce_serang.R
|
||||
import com.alya.ecommerce_serang.data.api.dto.CompletedOrderRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.OrdersItem
|
||||
import com.alya.ecommerce_serang.data.api.dto.ReviewUIItem
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.OrderListItemsItem
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.Orders
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||
import com.alya.ecommerce_serang.data.repository.OrderRepository
|
||||
import com.alya.ecommerce_serang.databinding.ActivityDetailOrderStatusBinding
|
||||
import com.alya.ecommerce_serang.ui.order.detail.PaymentActivity
|
||||
import com.alya.ecommerce_serang.ui.order.review.CreateReviewActivity
|
||||
import com.alya.ecommerce_serang.ui.product.ReviewProductActivity
|
||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||
import com.alya.ecommerce_serang.utils.SessionManager
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import com.google.gson.Gson
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
import java.util.TimeZone
|
||||
|
||||
class DetailOrderStatusActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var binding: ActivityDetailOrderStatusBinding
|
||||
private lateinit var sessionManager: SessionManager
|
||||
private var orderId: Int = -1
|
||||
private var orderStatus: String = ""
|
||||
private val orders = mutableListOf<OrdersItem>()
|
||||
private var selectedImageUri: Uri? = null
|
||||
|
||||
private var cancelDialog: Dialog? = null
|
||||
private var dialogImageView: ImageView? = null
|
||||
private var dialogSelectTextView: TextView? = null
|
||||
|
||||
private val viewModel: DetailOrderViewModel by viewModels {
|
||||
BaseViewModelFactory {
|
||||
val apiService = ApiConfig.getApiService(sessionManager)
|
||||
val orderRepository = OrderRepository(apiService)
|
||||
DetailOrderViewModel(orderRepository)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Log.d(TAG, "onCreate: Starting activity initialization")
|
||||
|
||||
binding = ActivityDetailOrderStatusBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
sessionManager = SessionManager(this)
|
||||
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
enableEdgeToEdge()
|
||||
|
||||
// Apply insets to your root layout
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view, windowInsets ->
|
||||
val systemBars = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
view.setPadding(
|
||||
systemBars.left,
|
||||
systemBars.top,
|
||||
systemBars.right,
|
||||
systemBars.bottom
|
||||
)
|
||||
windowInsets
|
||||
}
|
||||
|
||||
orderId = intent.getIntExtra("ORDER_ID", -1)
|
||||
orderStatus = intent.getStringExtra("ORDER_STATUS") ?: ""
|
||||
|
||||
Log.d(TAG, "onCreate: orderID=$orderId, orderStatus=$orderStatus")
|
||||
|
||||
if (orderId == -1) {
|
||||
Log.e(TAG, "onCreate: Invalid order ID received")
|
||||
Toast.makeText(this, "Invalid order ID", Toast.LENGTH_SHORT).show()
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
setupObservers()
|
||||
loadOrderDetails()
|
||||
|
||||
Log.d(TAG, "onCreate: Activity initialization completed")
|
||||
}
|
||||
|
||||
private fun setupObservers() {
|
||||
Log.d(TAG, "setupObservers: Setting up LiveData observers")
|
||||
|
||||
// Observe order details
|
||||
viewModel.orderDetails.observe(this) { orders ->
|
||||
if (orders != null) {
|
||||
Log.d(TAG, "Observer: orderDetails received, orderId=${orders.orderId}")
|
||||
populateOrderDetails(orders)
|
||||
} else {
|
||||
Log.w(TAG, "Observer: orderDetails is null")
|
||||
}
|
||||
}
|
||||
|
||||
// Observe loading state
|
||||
viewModel.isLoading.observe(this) { isLoading ->
|
||||
Log.d(TAG, "Observer: isLoading=$isLoading")
|
||||
binding.progressBar.visibility = if (isLoading) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
// Observe error messages
|
||||
viewModel.error.observe(this) { errorMsg ->
|
||||
if (!errorMsg.isNullOrEmpty()) {
|
||||
Log.e(TAG, "Observer: Error received: $errorMsg")
|
||||
Toast.makeText(this, errorMsg, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
// Observe success status
|
||||
viewModel.isSuccess.observe(this) { isSuccess ->
|
||||
Log.d(TAG, "Observer: isSuccess=$isSuccess")
|
||||
}
|
||||
|
||||
// Observe messages
|
||||
viewModel.message.observe(this) { message ->
|
||||
if (!message.isNullOrEmpty()) {
|
||||
Log.d(TAG, "Observer: Message: $message")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadOrderDetails() {
|
||||
Log.d(TAG, "loadOrderDetails: Requesting order details for orderId=$orderId")
|
||||
viewModel.getOrderDetails(orderId)
|
||||
}
|
||||
|
||||
private fun populateOrderDetails(orders: Orders) {
|
||||
Log.d(TAG, "populateOrderDetails: Populating UI with order data")
|
||||
|
||||
try {
|
||||
// Set order date and payment deadline
|
||||
binding.tvOrderDate.text = formatDate(orders.createdAt)
|
||||
binding.tvPaymentDeadline.text = formatDatePay(orders.updatedAt)
|
||||
|
||||
Log.d(TAG, "populateOrderDetails: Order created at ${orders.createdAt}, formatted as ${binding.tvOrderDate.text}")
|
||||
|
||||
// Set address information
|
||||
binding.tvRecipientName.text = orders.detail
|
||||
binding.tvAddress.text = "${orders.street}, ${orders.subdistrict}"
|
||||
|
||||
Log.d(TAG, "populateOrderDetails: Shipping to ${orders.detail} at ${orders.street}")
|
||||
|
||||
// Set courier info
|
||||
binding.tvCourier.text = "${orders.courier} ${orders.service}"
|
||||
|
||||
Log.d(TAG, "populateOrderDetails: Courier=${orders.courier}, Service=${orders.service}")
|
||||
|
||||
// Set product details using RecyclerView
|
||||
Log.d(TAG, "populateOrderDetails: Setting up products RecyclerView with ${orders.orderItems.size} items")
|
||||
setupProductsRecyclerView(orders.orderItems)
|
||||
|
||||
// Set payment method
|
||||
binding.tvPaymentMethod.text = "Bank Transfer - ${orders.payInfoName ?: "BCA"}"
|
||||
|
||||
Log.d(TAG, "populateOrderDetails: Payment method=${orders.payInfoName ?: "BCA"}")
|
||||
|
||||
// Set subtotal, shipping cost, and total
|
||||
val subtotal = orders.totalAmount?.toIntOrNull()?.minus(orders.shipmentPrice.toIntOrNull() ?: 0) ?: 0
|
||||
binding.tvSubtotal.text = "Rp$subtotal"
|
||||
binding.tvShippingCost.text = "Rp${orders.shipmentPrice}"
|
||||
binding.tvTotal.text = "Rp${orders.totalAmount}"
|
||||
|
||||
Log.d(TAG, "populateOrderDetails: Subtotal=$subtotal, Shipping=${orders.shipmentPrice}, Total=${orders.totalAmount}")
|
||||
|
||||
// Adjust buttons based on order status
|
||||
Log.d(TAG, "populateOrderDetails: Adjusting buttons for status=$orderStatus")
|
||||
adjustButtonsBasedOnStatus(orders, orderStatus)
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "populateOrderDetails: Error while populating UI", e)
|
||||
Toast.makeText(this, "Error loading order details: ${e.message}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupProductsRecyclerView(orderItems: List<OrderListItemsItem>) {
|
||||
Log.d(TAG, "setupProductsRecyclerView: Setting up RecyclerView with ${orderItems.size} items")
|
||||
|
||||
val adapter = DetailOrderItemsAdapter()
|
||||
binding.rvOrderItems.apply {
|
||||
layoutManager = LinearLayoutManager(this@DetailOrderStatusActivity)
|
||||
this.adapter = adapter
|
||||
}
|
||||
adapter.submitList(orderItems)
|
||||
}
|
||||
|
||||
private fun adjustButtonsBasedOnStatus(orders: Orders, status: String) {
|
||||
Log.d(TAG, "adjustButtonsBasedOnStatus: Adjusting UI for status=$status")
|
||||
|
||||
// Reset button visibility first
|
||||
binding.btnPrimary.visibility = View.GONE
|
||||
binding.btnSecondary.visibility = View.GONE
|
||||
|
||||
// Set status header
|
||||
val statusText = when(status) {
|
||||
"pending" -> "Belum Bayar"
|
||||
"unpaid" -> "Belum Bayar"
|
||||
"processed" -> "Diproses"
|
||||
"shipped" -> "Dikirim"
|
||||
"delivered" -> "Diterima"
|
||||
"completed" -> "Selesai"
|
||||
"canceled" -> "Dibatalkan"
|
||||
else -> "Detail Pesanan"
|
||||
}
|
||||
|
||||
binding.tvStatusHeader.text = statusText
|
||||
Log.d(TAG, "adjustButtonsBasedOnStatus: Status header set to '$statusText'")
|
||||
|
||||
when (status) {
|
||||
"pending", "unpaid" -> {
|
||||
Log.d(TAG, "adjustButtonsBasedOnStatus: Setting up UI for pending/unpaid order")
|
||||
|
||||
// Show status note
|
||||
binding.tvStatusNote.visibility = View.VISIBLE
|
||||
binding.tvStatusNote.text = "Pesanan ini harus dibayar sebelum ${formatDatePay(orders.updatedAt)}"
|
||||
|
||||
// Set buttons
|
||||
binding.btnSecondary.apply {
|
||||
visibility = View.VISIBLE
|
||||
text = "Batalkan Pesanan"
|
||||
setOnClickListener {
|
||||
Log.d(TAG, "Cancel Order button clicked")
|
||||
showCancelOrderDialog(orders.orderId.toString())
|
||||
}
|
||||
}
|
||||
|
||||
binding.btnPrimary.apply {
|
||||
visibility = View.VISIBLE
|
||||
text = "Bayar Sekarang"
|
||||
setOnClickListener {
|
||||
Log.d(TAG, "Pay Now button clicked, navigating to PaymentActivity")
|
||||
val intent = Intent(this@DetailOrderStatusActivity, PaymentActivity::class.java)
|
||||
intent.putExtra("ORDER_ID", orders.orderId)
|
||||
intent.putExtra("ORDER_PAYMENT_ID", orders.paymentInfoId)
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"processed" -> {
|
||||
Log.d(TAG, "adjustButtonsBasedOnStatus: Setting up UI for processed order")
|
||||
|
||||
binding.tvStatusNote.visibility = View.VISIBLE
|
||||
binding.tvStatusNote.text = "Penjual sedang memproses pesanan Anda"
|
||||
|
||||
binding.btnSecondary.apply {
|
||||
visibility = View.VISIBLE
|
||||
text = "Batalkan Pesanan"
|
||||
setOnClickListener {
|
||||
Log.d(TAG, "Cancel Order button clicked for processed order")
|
||||
showCancelOrderDialog(orders.orderId.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"shipped" -> {
|
||||
Log.d(TAG, "adjustButtonsBasedOnStatus: Setting up UI for shipped order")
|
||||
|
||||
binding.tvStatusNote.visibility = View.VISIBLE
|
||||
binding.tvStatusNote.text = "Pesanan Anda sedang dalam perjalanan. Akan sampai sekitar ${formatShipmentDate(orders.updatedAt, orders.etd ?: "0")}"
|
||||
|
||||
binding.btnSecondary.apply {
|
||||
visibility = View.VISIBLE
|
||||
text = "Ajukan Komplain"
|
||||
setOnClickListener {
|
||||
Log.d(TAG, "Complaint button clicked")
|
||||
showCancelOrderDialog(orders.orderId.toString()) // For now, reuse the cancel dialog
|
||||
}
|
||||
}
|
||||
|
||||
binding.btnPrimary.apply {
|
||||
visibility = View.VISIBLE
|
||||
text = "Terima Pesanan"
|
||||
|
||||
val completedOrderRequest = CompletedOrderRequest(
|
||||
orderId = orders.orderId,
|
||||
statusComplete = "completed"
|
||||
)
|
||||
|
||||
setOnClickListener {
|
||||
Log.d(TAG, "Confirm receipt button clicked, marking order as completed")
|
||||
viewModel.confirmOrderCompleted(completedOrderRequest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"delivered", "completed" -> {
|
||||
Log.d(TAG, "adjustButtonsBasedOnStatus: Setting up UI for delivered/completed order")
|
||||
|
||||
binding.tvStatusNote.visibility = View.VISIBLE
|
||||
binding.tvStatusNote.text = "Pesanan telah selesai"
|
||||
|
||||
binding.btnPrimary.apply {
|
||||
visibility = View.VISIBLE
|
||||
text = "Beri Ulasan"
|
||||
setOnClickListener {
|
||||
Log.d(TAG, "Review button clicked")
|
||||
addReviewForOrder(orders)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"canceled" -> {
|
||||
Log.d(TAG, "adjustButtonsBasedOnStatus: Setting up UI for canceled order")
|
||||
|
||||
binding.tvStatusNote.visibility = View.VISIBLE
|
||||
binding.tvStatusNote.text = "Pesanan dibatalkan: ${orders.cancelReason ?: "Alasan tidak diberikan"}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addReviewForOrder(orders: Orders) {
|
||||
Log.d(TAG, "addReviewForOrder: Preparing to add review for order ${orders.orderId}")
|
||||
|
||||
val orderItems = orders.orderItems
|
||||
|
||||
if (orderItems.isNotEmpty()) {
|
||||
Log.d(TAG, "addReviewForOrder: Found ${orderItems.size} items to review")
|
||||
|
||||
// For single item review
|
||||
if (orderItems.size == 1) {
|
||||
val item = orderItems[0]
|
||||
Log.d(TAG, "addReviewForOrder: Launching single item review for orderItemId=${item.orderItemId}")
|
||||
|
||||
val intent = Intent(this, CreateReviewActivity::class.java).apply {
|
||||
putExtra("order_item_id", item.orderItemId)
|
||||
putExtra("product_name", item.productName)
|
||||
putExtra("product_image", item.productImage)
|
||||
}
|
||||
startActivityForResult(intent, REQUEST_CODE_REVIEW)
|
||||
}
|
||||
// For multiple items
|
||||
else {
|
||||
Log.d(TAG, "addReviewForOrder: Launching multi-item review with ${orderItems.size} items")
|
||||
|
||||
val reviewItems = orderItems.map { item ->
|
||||
ReviewUIItem(
|
||||
orderItemId = item.orderItemId,
|
||||
productName = item.productName,
|
||||
productImage = item.productImage
|
||||
)
|
||||
}
|
||||
|
||||
val itemsJson = Gson().toJson(reviewItems)
|
||||
Log.d(TAG, "addReviewForOrder: JSON prepared for items: ${itemsJson.take(100)}...")
|
||||
|
||||
val intent = Intent(this, ReviewProductActivity::class.java).apply {
|
||||
putExtra("order_items", itemsJson)
|
||||
}
|
||||
startActivityForResult(intent, REQUEST_CODE_REVIEW)
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "addReviewForOrder: No items found to review")
|
||||
Toast.makeText(this, "No items to review", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showCancelOrderDialog(orderId: String) {
|
||||
Log.d(TAG, "showCancelOrderDialog: Showing dialog for orderId=$orderId")
|
||||
|
||||
val dialog = Dialog(this)
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||
dialog.setContentView(R.layout.dialog_cancel_order)
|
||||
dialog.setCancelable(true)
|
||||
|
||||
// Store dialog reference
|
||||
cancelDialog = dialog
|
||||
|
||||
// Set the dialog width to match parent
|
||||
val window = dialog.window
|
||||
window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
|
||||
// Get references to the views in the dialog
|
||||
val spinnerCancelReason = dialog.findViewById<AutoCompleteTextView>(R.id.spinnerCancelReason)
|
||||
val tilCancelReason = dialog.findViewById<TextInputLayout>(R.id.tilCancelReason)
|
||||
val btnCancelDialog = dialog.findViewById<MaterialButton>(R.id.btnCancelDialog)
|
||||
val btnConfirmCancel = dialog.findViewById<MaterialButton>(R.id.btnConfirmCancel)
|
||||
val ivComplaintImage = dialog.findViewById<ImageView>(R.id.ivComplaintImage)
|
||||
val tvSelectImage = dialog.findViewById<TextView>(R.id.tvSelectImage)
|
||||
|
||||
dialogImageView = ivComplaintImage
|
||||
dialogSelectTextView = tvSelectImage
|
||||
|
||||
// Set up the reasons dropdown
|
||||
val reasons = this.resources.getStringArray(R.array.cancellation_reasons)
|
||||
Log.d(TAG, "showCancelOrderDialog: Setting up dropdown with ${reasons.size} reasons")
|
||||
|
||||
val adapter = ArrayAdapter(this, android.R.layout.simple_dropdown_item_1line, reasons)
|
||||
spinnerCancelReason.setAdapter(adapter)
|
||||
|
||||
// For storing the selected image URI
|
||||
var selectedImageUri: Uri? = null
|
||||
|
||||
// Set click listener for image selection
|
||||
ivComplaintImage.setOnClickListener {
|
||||
Log.d(TAG, "showCancelOrderDialog: Image selection clicked")
|
||||
|
||||
// Create an intent to open the image picker
|
||||
val galleryIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
|
||||
(this as? Activity)?.startActivityForResult(galleryIntent, REQUEST_IMAGE_PICK)
|
||||
|
||||
// Set up result handler in the activity
|
||||
val activity = this as? Activity
|
||||
activity?.let {
|
||||
// Remove any existing callbacks to avoid memory leaks
|
||||
if (imagePickCallback != null) {
|
||||
imagePickCallback = null
|
||||
}
|
||||
|
||||
// Create a new callback for this specific dialog
|
||||
imagePickCallback = { uri ->
|
||||
Log.d(TAG, "imagePickCallback: Image selected, URI=$uri")
|
||||
selectedImageUri = uri
|
||||
|
||||
// Load and display the selected image
|
||||
ivComplaintImage.setImageURI(uri)
|
||||
tvSelectImage.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set click listeners for buttons
|
||||
btnCancelDialog.setOnClickListener {
|
||||
Log.d(TAG, "showCancelOrderDialog: Cancel button clicked, dismissing dialog")
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
btnConfirmCancel.setOnClickListener {
|
||||
val reason = spinnerCancelReason.text.toString().trim()
|
||||
Log.d(TAG, "showCancelOrderDialog: Confirm cancel clicked with reason: $reason")
|
||||
|
||||
if (reason.isEmpty()) {
|
||||
Log.w(TAG, "showCancelOrderDialog: No reason selected")
|
||||
tilCancelReason.error = this.getString(R.string.please_select_cancellation_reason)
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
// Clear error if any
|
||||
tilCancelReason.error = null
|
||||
|
||||
// Convert selected image to file if available
|
||||
val imageFile = selectedImageUri?.let { uri ->
|
||||
try {
|
||||
Log.d(TAG, "showCancelOrderDialog: Converting URI to file: $uri")
|
||||
// Get the file path from URI
|
||||
val filePathColumn = arrayOf(MediaStore.Images.Media.DATA)
|
||||
val cursor = this.contentResolver.query(uri, filePathColumn, null, null, null)
|
||||
cursor?.use {
|
||||
if (it.moveToFirst()) {
|
||||
val columnIndex = it.getColumnIndex(filePathColumn[0])
|
||||
val filePath = it.getString(columnIndex)
|
||||
Log.d(TAG, "showCancelOrderDialog: File path: $filePath")
|
||||
return@let File(filePath)
|
||||
}
|
||||
}
|
||||
Log.w(TAG, "showCancelOrderDialog: Failed to get file path from URI")
|
||||
null
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "showCancelOrderDialog: Error getting file from URI: ${e.message}", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
// Show loading indicator
|
||||
Log.d(TAG, "showCancelOrderDialog: Showing loading indicator")
|
||||
val loadingView = View(this).apply {
|
||||
layoutParams = ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
setBackgroundColor(Color.parseColor("#80000000"))
|
||||
}
|
||||
|
||||
dialog.addContentView(loadingView, loadingView.layoutParams)
|
||||
|
||||
// Call the ViewModel to cancel the order with image
|
||||
Log.d(TAG, "showCancelOrderDialog: Calling cancelOrderWithImage for orderId=$orderId")
|
||||
viewModel.cancelOrderWithImage(orderId.toInt(), reason, imageFile)
|
||||
|
||||
// Observe for success/failure
|
||||
viewModel.isSuccess.observe(this) { isSuccess ->
|
||||
Log.d(TAG, "showCancelOrderDialog observer: isSuccess=$isSuccess")
|
||||
|
||||
if (isSuccess) {
|
||||
Log.d(TAG, "showCancelOrderDialog: Order canceled successfully")
|
||||
Toast.makeText(this, getString(R.string.order_canceled_successfully), Toast.LENGTH_SHORT).show()
|
||||
dialog.dismiss()
|
||||
|
||||
// Set result and finish
|
||||
setResult(RESULT_OK)
|
||||
finish()
|
||||
} else {
|
||||
Log.e(TAG, "showCancelOrderDialog: Failed to cancel order: ${viewModel.message.value}")
|
||||
Toast.makeText(this, viewModel.message.value ?: getString(R.string.failed_to_cancel_order), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "showCancelOrderDialog: Dialog setup complete, showing dialog")
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
private fun formatDate(dateString: String): String {
|
||||
Log.d(TAG, "formatDate: Formatting date: $dateString")
|
||||
|
||||
return try {
|
||||
val inputFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault())
|
||||
inputFormat.timeZone = TimeZone.getTimeZone("UTC")
|
||||
|
||||
val outputFormat = SimpleDateFormat("HH:mm dd MMMM yyyy", Locale("id", "ID"))
|
||||
|
||||
val date = inputFormat.parse(dateString)
|
||||
|
||||
date?.let {
|
||||
val calendar = Calendar.getInstance()
|
||||
calendar.time = it
|
||||
calendar.set(Calendar.HOUR_OF_DAY, 23)
|
||||
calendar.set(Calendar.MINUTE, 59)
|
||||
|
||||
val formatted = outputFormat.format(calendar.time)
|
||||
Log.d(TAG, "formatDate: Formatted date: $formatted")
|
||||
formatted
|
||||
} ?: dateString
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "formatDate: Error formatting date: ${e.message}", e)
|
||||
dateString
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatDatePay(dateString: String): String {
|
||||
Log.d(TAG, "formatDatePay: Formatting payment date: $dateString")
|
||||
|
||||
return try {
|
||||
// Parse the ISO 8601 date
|
||||
val isoDateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault())
|
||||
isoDateFormat.timeZone = TimeZone.getTimeZone("UTC")
|
||||
|
||||
val createdDate = isoDateFormat.parse(dateString)
|
||||
|
||||
// Add 24 hours to get due date
|
||||
val calendar = Calendar.getInstance()
|
||||
calendar.time = createdDate
|
||||
calendar.add(Calendar.HOUR, 24)
|
||||
val dueDate = calendar.time
|
||||
|
||||
// Format due date for display
|
||||
val dueDateFormat = SimpleDateFormat("dd MMM yyyy", Locale.getDefault())
|
||||
val formatted = dueDateFormat.format(calendar.time)
|
||||
|
||||
Log.d(TAG, "formatDatePay: Formatted payment date: $formatted")
|
||||
formatted
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "formatDatePay: Error formatting date: ${e.message}", e)
|
||||
dateString
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatShipmentDate(dateString: String, estimateString: String): String {
|
||||
Log.d(TAG, "formatShipmentDate: Formatting shipment date: $dateString with ETD: $estimateString")
|
||||
|
||||
return try {
|
||||
// Safely parse the estimate to Int
|
||||
val estimate = if (estimateString.isNullOrEmpty()) 0 else estimateString.toInt()
|
||||
Log.d(TAG, "formatShipmentDate: Parsed ETD as $estimate days")
|
||||
|
||||
// Parse the input date
|
||||
val inputFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault())
|
||||
inputFormat.timeZone = TimeZone.getTimeZone("UTC")
|
||||
|
||||
// Output format
|
||||
val outputFormat = SimpleDateFormat("dd MMMM yyyy", Locale("id", "ID"))
|
||||
|
||||
// Parse the input date
|
||||
val date = inputFormat.parse(dateString)
|
||||
|
||||
date?.let {
|
||||
val calendar = Calendar.getInstance()
|
||||
calendar.time = it
|
||||
|
||||
// Add estimated days
|
||||
calendar.add(Calendar.DAY_OF_MONTH, estimate)
|
||||
val formatted = outputFormat.format(calendar.time)
|
||||
|
||||
Log.d(TAG, "formatShipmentDate: Estimated arrival date: $formatted")
|
||||
formatted
|
||||
} ?: dateString
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "formatShipmentDate: Error formatting shipment date: ${e.message}", e)
|
||||
dateString
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
Log.d(TAG, "onActivityResult: requestCode=$requestCode, resultCode=$resultCode")
|
||||
|
||||
when (requestCode) {
|
||||
REQUEST_IMAGE_PICK -> {
|
||||
if (resultCode == RESULT_OK && data != null) {
|
||||
// Get the selected image URI
|
||||
selectedImageUri = data.data
|
||||
Log.d(TAG, "onActivityResult: Image selected, URI=$selectedImageUri")
|
||||
|
||||
// Update the image view in the dialog if the dialog is still showing
|
||||
if (cancelDialog?.isShowing == true) {
|
||||
Log.d(TAG, "onActivityResult: Updating image in dialog")
|
||||
dialogImageView?.setImageURI(selectedImageUri)
|
||||
dialogSelectTextView?.visibility = View.GONE
|
||||
} else {
|
||||
Log.w(TAG, "onActivityResult: Dialog is not showing, cannot update image")
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "onActivityResult: Image selection canceled or failed")
|
||||
}
|
||||
}
|
||||
REQUEST_CODE_REVIEW -> {
|
||||
if (resultCode == RESULT_OK) {
|
||||
// Review submitted successfully
|
||||
Log.d(TAG, "onActivityResult: Review submitted successfully")
|
||||
Toast.makeText(this, "Review submitted successfully", Toast.LENGTH_SHORT).show()
|
||||
|
||||
// Refresh order details
|
||||
loadOrderDetails()
|
||||
|
||||
// Set result to notify parent activity
|
||||
setResult(RESULT_OK)
|
||||
} else {
|
||||
Log.w(TAG, "onActivityResult: Review submission canceled or failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
Log.d(TAG, "onDestroy: Cleaning up references")
|
||||
super.onDestroy()
|
||||
// Clean up references
|
||||
cancelDialog = null
|
||||
dialogImageView = null
|
||||
dialogSelectTextView = null
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
private const val REQUEST_IMAGE_PICK = 100
|
||||
private const val REQUEST_CODE_REVIEW = 101
|
||||
private const val TAG = "DetailOrderActivity" // Add tag for logging
|
||||
|
||||
|
||||
private var imagePickCallback: ((Uri) -> Unit)? = null
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
package com.alya.ecommerce_serang.ui.order.history.detailorder
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.alya.ecommerce_serang.data.api.dto.CompletedOrderRequest
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.Orders
|
||||
import com.alya.ecommerce_serang.data.repository.OrderRepository
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
|
||||
class DetailOrderViewModel(private val orderRepository: OrderRepository): ViewModel() {
|
||||
|
||||
private val _orderDetails = MutableLiveData<Orders>()
|
||||
val orderDetails: LiveData<Orders> = _orderDetails
|
||||
|
||||
private val _isLoading = MutableLiveData<Boolean>()
|
||||
val isLoading: LiveData<Boolean> = _isLoading
|
||||
|
||||
private val _error = MutableLiveData<String>()
|
||||
val error: LiveData<String> = _error
|
||||
|
||||
private val _isSuccess = MutableLiveData<Boolean>()
|
||||
val isSuccess: LiveData<Boolean> = _isSuccess
|
||||
|
||||
private val _message = MutableLiveData<String>()
|
||||
val message: LiveData<String> = _message
|
||||
|
||||
fun getOrderDetails(orderId: Int) {
|
||||
_isLoading.value = true
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val response = orderRepository.getOrderDetails(orderId)
|
||||
if (response != null) {
|
||||
_orderDetails.value = response.orders
|
||||
} else {
|
||||
_error.value = "Failed to load order details"
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
_error.value = "Error: ${e.message}"
|
||||
Log.e("DetailOrderViewModel", "Error loading order details", e)
|
||||
} finally {
|
||||
_isLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun confirmOrderCompleted(detailOrderRequest: CompletedOrderRequest) {
|
||||
_isLoading.value = true
|
||||
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
orderRepository.confirmOrderCompleted(detailOrderRequest)
|
||||
_isSuccess.value = true
|
||||
_message.value = "Order status updated successfully"
|
||||
|
||||
getOrderDetails(detailOrderRequest.orderId)
|
||||
|
||||
} catch (e: Exception) {
|
||||
_isSuccess.value = false
|
||||
_message.value = "Error: ${e.message}"
|
||||
Log.e("DetailOrderViewModel", "Error updating order status", e)
|
||||
} finally {
|
||||
_isLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun cancelOrderWithImage(orderId: Int, reason: String, imageFile: File?) {
|
||||
_isLoading.value = true
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
orderRepository.submitComplaint(orderId.toString(), reason, imageFile)
|
||||
_isSuccess.value = true
|
||||
_message.value = "Order canceled successfully"
|
||||
|
||||
} catch (e: Exception) {
|
||||
_isSuccess.value = false
|
||||
_message.value = "Error: ${e.message}"
|
||||
Log.e("DetailOrderViewModel", "Error canceling order", e)
|
||||
} finally {
|
||||
_isLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
429
app/src/main/res/layout/activity_detail_order_status.xml
Normal file
429
app/src/main/res/layout/activity_detail_order_status.xml
Normal file
@ -0,0 +1,429 @@
|
||||
<?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"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ui.order.history.detailorder.DetailOrderStatusActivity">
|
||||
|
||||
<include
|
||||
android:id="@+id/header"
|
||||
layout="@layout/header"/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/scrollViewDetail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/buttonLayout"
|
||||
app:layout_constraintTop_toBottomOf="@+id/header">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- Status header -->
|
||||
<!-- Order Status Card -->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:cardElevation="2dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvStatusHeader"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/blue_500"
|
||||
android:paddingVertical="8dp"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:text="Belum Bayar"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<!-- Status Note (if any) -->
|
||||
<TextView
|
||||
android:id="@+id/tvStatusNote"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="8dp"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:text="Pesanan ini harus dibayar sebelum 18 November 2024"
|
||||
android:textColor="@color/blue_400"
|
||||
android:textSize="14sp"
|
||||
android:visibility="visible"/>
|
||||
|
||||
<!-- Order dates -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/white"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvOrderDateLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:text="Tanggal Pesanan:"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvOrderDate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="15 November 2024"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintStart_toStartOf="@id/tvOrderDateLabel"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvOrderDateLabel" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvPaymentDeadlineLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:text="Batas Pembayaran:"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintTop_toTopOf="@id/tvOrderDateLabel"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvPaymentDeadline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="18 November 2024"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintStart_toStartOf="@id/tvPaymentDeadlineLabel"
|
||||
app:layout_constraintTop_toTopOf="@id/tvOrderDate" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="8dp"
|
||||
android:background="@color/light_gray" />
|
||||
|
||||
<!-- Shipping Information -->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:cardElevation="2dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Informasi Pengiriman"
|
||||
android:textColor="@color/black"
|
||||
android:fontFamily="@font/dmsans_bold"
|
||||
android:textSize="16sp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:text="Alamat"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvRecipientName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="Gracia 081234533453"
|
||||
android:fontFamily="@font/dmsans_italic"
|
||||
android:textColor="@color/black_400"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvAddress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Jl. Pegangsaan Timur"
|
||||
android:fontFamily="@font/dmsans_italic"
|
||||
android:textColor="@color/black_400"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="Kurir"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCourier"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="JNE Reguler"
|
||||
android:fontFamily="@font/dmsans_italic"
|
||||
android:textColor="@color/black_400"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="8dp"
|
||||
android:background="@color/light_gray" />
|
||||
|
||||
<!-- Order Items -->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:cardElevation="2dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvStoreName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="SnackEnak"
|
||||
android:fontFamily="@font/dmsans_semibold"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvOrderItems"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:nestedScrollingEnabled="false"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:itemCount="2"
|
||||
tools:listitem="@layout/item_order_detail_product" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<!-- Payment Method Card -->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:cardElevation="2dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Metode Pembayaran"
|
||||
android:textColor="@color/black"
|
||||
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvPaymentMethod"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="Bank Transfer - Bank BCA"
|
||||
android:textColor="@color/black_400"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<!-- Order Summary Card -->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:cardElevation="2dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Subtotal"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="1 item"
|
||||
android:textColor="@color/black_400"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSubtotal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Rp65.000"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Biaya Pengiriman"
|
||||
android:textColor="@color/black_400"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvShippingCost"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Rp15.000"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@color/light_gray" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Total"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTotal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Rp75.000"
|
||||
android:textColor="@color/blue_500"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<!-- Add space at the bottom for buttons -->
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="80dp" />
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<!-- Buttons at the bottom -->
|
||||
<LinearLayout
|
||||
android:id="@+id/buttonLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/white"
|
||||
android:elevation="8dp"
|
||||
android:orientation="horizontal"
|
||||
android:padding="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/scrollViewDetail">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnSecondary"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_weight="1"
|
||||
android:text="Batalkan Pesanan"
|
||||
android:textAllCaps="false"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnPrimary"
|
||||
style="@style/Widget.MaterialComponents.Button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_weight="1"
|
||||
android:text="Bayar Sekarang"
|
||||
android:textAllCaps="false"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
61
app/src/main/res/layout/item_order_detail_product.xml
Normal file
61
app/src/main/res/layout/item_order_detail_product.xml
Normal file
@ -0,0 +1,61 @@
|
||||
<?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"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivProduct"
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="64dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:elevation="4dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@drawable/placeholder_image" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvProductName"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:text="Keripik Balado"
|
||||
android:textColor="@color/black"
|
||||
android:fontFamily="@font/dmsans_regular"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/ivProduct"
|
||||
app:layout_constraintTop_toTopOf="@+id/ivProduct" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvQuantity"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="1 buah"
|
||||
android:fontFamily="@font/dmsans_regular"
|
||||
android:textColor="@color/black_200"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintStart_toEndOf="@+id/ivProduct"
|
||||
app:layout_constraintTop_toBottomOf="@+id/tvProductName" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvPrice"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="Rp65.000"
|
||||
android:fontFamily="@font/dmsans_regular"
|
||||
android:textColor="@color/blue_500"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintStart_toEndOf="@+id/ivProduct"
|
||||
app:layout_constraintTop_toBottomOf="@+id/tvQuantity" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
Reference in New Issue
Block a user