update display product list and paid status

This commit is contained in:
shaulascr
2025-06-17 16:20:30 +07:00
parent fe331e7bb3
commit 95d5439050
14 changed files with 181 additions and 152 deletions

View File

@ -12,7 +12,6 @@ import com.bumptech.glide.Glide
class HomeCategoryAdapter(
private var categories:List<CategoryItem>,
//A lambda function that will be invoked when a category item is clicked.
private val onClick:(category:CategoryItem) -> Unit
): RecyclerView.Adapter<HomeCategoryAdapter.ViewHolder>() {

View File

@ -15,7 +15,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.alya.ecommerce_serang.data.api.dto.CategoryItem
import com.alya.ecommerce_serang.data.api.dto.ProductsItem
import com.alya.ecommerce_serang.data.api.response.customer.product.StoreItem
@ -83,20 +83,15 @@ class HomeFragment : Fragment() {
binding.newProducts.apply {
adapter = productAdapter
layoutManager = GridLayoutManager(
context,
2,
LinearLayoutManager.HORIZONTAL,
false
)
layoutManager = GridLayoutManager(requireContext(), 2)
}
binding.categories.apply {
adapter = categoryAdapter
layoutManager = GridLayoutManager(
context,
3,
LinearLayoutManager.HORIZONTAL,
3, // 3 columns
RecyclerView.VERTICAL, // vertical layout
false
)
}

View File

@ -91,7 +91,7 @@ class HistoryViewModel(private val repository: OrderRepository) : ViewModel() {
private suspend fun getAllOrdersCombined() {
try {
val allStatuses = listOf("pending", "unpaid", "processed", "shipped", "completed", "canceled")
val allStatuses = listOf("unpaid", "paid", "processed", "shipped", "completed", "canceled")
val allOrders = mutableListOf<OrdersItem>()
// Use coroutineScope to allow launching async blocks
@ -211,10 +211,7 @@ class HistoryViewModel(private val repository: OrderRepository) : ViewModel() {
fun refreshOrders(status: String = "all") {
Log.d(TAG, "Refreshing orders with status: $status")
// Clear current orders before fetching new ones
_orders.value = ViewState.Loading
// Re-fetch the orders with the current status
// Don't set Loading here if you want to show current data while refreshing
getOrderList(status)
}
}

View File

@ -4,7 +4,6 @@ import android.app.Activity
import android.app.Dialog
import android.content.ContextWrapper
import android.content.Intent
import android.graphics.Color
import android.net.Uri
import android.provider.MediaStore
import android.util.Log
@ -15,10 +14,10 @@ import android.view.Window
import android.widget.ArrayAdapter
import android.widget.AutoCompleteTextView
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import android.widget.Toast
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Observer
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
@ -40,9 +39,16 @@ import java.util.TimeZone
class OrderHistoryAdapter(
private val onOrderClickListener: (OrdersItem) -> Unit,
private val viewModel: HistoryViewModel // Add this parameter
private val viewModel: HistoryViewModel,
private val callbacks: OrderActionCallbacks
) : RecyclerView.Adapter<OrderHistoryAdapter.OrderViewHolder>() {
interface OrderActionCallbacks {
fun onOrderCancelled(orderId: String, success: Boolean, message: String)
fun onOrderCompleted(orderId: Int, success: Boolean, message: String)
fun onShowLoading(show: Boolean)
}
private val orders = mutableListOf<OrdersItem>()
private var fragmentStatus: String = "all"
@ -140,28 +146,6 @@ class OrderHistoryAdapter(
deadlineLabel.visibility = View.GONE
when (status) {
"pending" -> {
statusOrder.apply {
visibility = View.VISIBLE
text = itemView.context.getString(R.string.pending_orders)
}
deadlineLabel.apply {
visibility = View.VISIBLE
text = itemView.context.getString(R.string.dl_pending)
}
btnLeft.apply {
visibility = View.VISIBLE
text = itemView.context.getString(R.string.canceled_order_btn)
setOnClickListener {
showCancelOrderBottomSheet(order.orderId)
viewModel.refreshOrders()
}
}
deadlineDate.apply {
visibility = View.VISIBLE
text = formatDate(order.createdAt)
}
}
"unpaid" -> {
statusOrder.apply {
visibility = View.VISIBLE
@ -176,7 +160,6 @@ class OrderHistoryAdapter(
text = itemView.context.getString(R.string.canceled_order_btn)
setOnClickListener {
showCancelOrderBottomSheet(order.orderId)
viewModel.refreshOrders()
}
}
@ -198,6 +181,28 @@ class OrderHistoryAdapter(
text = formatDatePay(order.updatedAt)
}
}
"paid" -> {
statusOrder.apply {
visibility = View.VISIBLE
text = itemView.context.getString(R.string.paid_orders)
}
deadlineLabel.apply {
visibility = View.VISIBLE
text = itemView.context.getString(R.string.dl_paid)
}
btnLeft.apply {
visibility = View.VISIBLE
text = itemView.context.getString(R.string.canceled_order_btn)
setOnClickListener {
showCancelOrderDialog(order.orderId.toString())
viewModel.refreshOrders()
}
}
// deadlineDate.apply {
// visibility = View.VISIBLE
// text = formatDatePay(order.updatedAt)
// }
}
"processed" -> {
// Untuk status processed, tampilkan "Hubungi Penjual"
statusOrder.apply {
@ -239,11 +244,14 @@ class OrderHistoryAdapter(
visibility = View.VISIBLE
text = itemView.context.getString(R.string.claim_order)
setOnClickListener {
// Handle click event
callbacks.onShowLoading(true)
// Call ViewModel
viewModel.confirmOrderCompleted(order.orderId, "completed")
viewModel.refreshOrders()
}
}
deadlineDate.apply {
visibility = View.VISIBLE
@ -454,52 +462,32 @@ class OrderHistoryAdapter(
}
}
// Show loading indicator
val loadingView = View(context).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
setBackgroundColor(Color.parseColor("#80000000"))
callbacks.onShowLoading(true)
val progressBar = ProgressBar(context).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
}
// addView(progressBar)
// (progressBar.layoutParams as? ViewGroup.MarginLayoutParams)?.apply {
// gravity = Gravity.CENTER
// }
}
dialog.addContentView(loadingView, loadingView.layoutParams)
// Call the ViewModel to cancel the order with image
// Call ViewModel method but don't observe here
viewModel.cancelOrderWithImage(orderId, reason, imageFile)
// Observe for success/failure
viewModel.isSuccess.observe(itemView.findViewTreeLifecycleOwner()!!) { isSuccess ->
// Remove loading indicator
(loadingView.parent as? ViewGroup)?.removeView(loadingView)
// Create a one-time observer that will be removed automatically
val observer = object : Observer<Boolean> {
override fun onChanged(isSuccess: Boolean) {
callbacks.onShowLoading(false)
if (isSuccess) {
Toast.makeText(context, context.getString(R.string.order_canceled_successfully), Toast.LENGTH_SHORT).show()
dialog.dismiss()
// Find the order in the list and remove it or update its status
val position = orders.indexOfFirst { it.orderId.toString() == orderId }
if (position != -1) {
orders.removeAt(position)
notifyItemRemoved(position)
notifyItemRangeChanged(position, orders.size)
if (isSuccess) {
val message = viewModel.message.value ?: context.getString(R.string.order_canceled_successfully)
callbacks.onOrderCancelled(orderId, true, message)
dialog.dismiss()
} else {
val message = viewModel.message.value ?: context.getString(R.string.failed_to_cancel_order)
callbacks.onOrderCancelled(orderId, false, message)
}
} else {
Toast.makeText(context, viewModel.message.value ?: context.getString(R.string.failed_to_cancel_order), Toast.LENGTH_SHORT).show()
// Remove this observer after first use
viewModel.isSuccess.removeObserver(this)
}
}
// Add observer only once
viewModel.isSuccess.observe(itemView.findViewTreeLifecycleOwner()!!, observer)
}
dialog.show()
}
@ -534,10 +522,7 @@ class OrderHistoryAdapter(
val bottomSheet = CancelOrderBottomSheet(
orderId = orderId,
onOrderCancelled = {
// Handle the successful cancellation
// Refresh the data
viewModel.refreshOrders() // Assuming there's a method to refresh orders
callbacks.onOrderCancelled(orderId.toString(), true, "Order cancelled successfully")
// Show a success message
Toast.makeText(context, "Order cancelled successfully", Toast.LENGTH_SHORT).show()
}

View File

@ -44,8 +44,8 @@ class OrderHistoryFragment : Fragment() {
TabLayoutMediator(binding.tabLayout, binding.viewPager) { tab, position ->
tab.text = when (position) {
0 -> getString(R.string.all_orders)
1 -> getString(R.string.pending_orders)
2 -> getString(R.string.unpaid_orders)
1 -> getString(R.string.unpaid_orders)
2 -> getString(R.string.paid_orders)
3 -> getString(R.string.processed_orders)
4 -> getString(R.string.shipped_orders)
5 -> getString(R.string.completed_orders)

View File

@ -21,7 +21,7 @@ import com.alya.ecommerce_serang.ui.order.history.detailorder.DetailOrderStatusA
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
import com.alya.ecommerce_serang.utils.SessionManager
class OrderListFragment : Fragment() {
class OrderListFragment : Fragment(), OrderHistoryAdapter.OrderActionCallbacks {
private var _binding: FragmentOrderListBinding? = null
private val binding get() = _binding!!
@ -72,6 +72,7 @@ class OrderListFragment : Fragment() {
setupRecyclerView()
observeOrderList()
observeViewModel()
observeOrderCompletionStatus()
loadOrders()
}
@ -81,7 +82,8 @@ class OrderListFragment : Fragment() {
onOrderClickListener = { order ->
navigateToOrderDetail(order)
},
viewModel = viewModel
viewModel = viewModel,
callbacks = this // Pass this fragment as callback
)
orderAdapter.setFragmentStatus(status)
@ -120,6 +122,40 @@ class OrderListFragment : Fragment() {
}
}
private fun observeViewModel() {
// Observe order completion
viewModel.orderCompletionStatus.observe(viewLifecycleOwner) { result ->
when (result) {
is Result.Success -> {
Toast.makeText(requireContext(), "Order completed successfully!", Toast.LENGTH_SHORT).show()
loadOrders() // Refresh here
}
is Result.Error -> {
Toast.makeText(requireContext(), "Failed: ${result.exception.message}", Toast.LENGTH_SHORT).show()
}
is Result.Loading -> {
// Show loading if needed
}
}
}
// Observe cancel order status
viewModel.cancelOrderStatus.observe(viewLifecycleOwner) { result ->
when (result) {
is Result.Success -> {
Toast.makeText(requireContext(), "Order cancelled successfully!", Toast.LENGTH_SHORT).show()
loadOrders() // Refresh here
}
is Result.Error -> {
Toast.makeText(requireContext(), "Failed to cancel: ${result.exception.message}", Toast.LENGTH_SHORT).show()
}
is Result.Loading -> {
// Show loading if needed
}
}
}
}
private fun loadOrders() {
// Simple - just call getOrderList for any status including "all"
viewModel.getOrderList(status)
@ -142,6 +178,30 @@ class OrderListFragment : Fragment() {
detailOrderLauncher.launch(intent)
}
override fun onOrderCancelled(orderId: String, success: Boolean, message: String) {
if (success) {
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
loadOrders() // Refresh the list
} else {
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
}
}
override fun onOrderCompleted(orderId: Int, success: Boolean, message: String) {
if (success) {
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
loadOrders() // Refresh the list
} else {
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
}
}
override fun onShowLoading(show: Boolean) {
binding.progressBar.visibility = if (show) View.VISIBLE else View.GONE
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null

View File

@ -11,8 +11,8 @@ class OrderViewPagerAdapter(
// Define all possible order statuses
private val orderStatuses = listOf(
"all", // All orders
"pending", // Menunggu Tagihan
"unpaid", // Belum Dibayar
"unpaid", // Menunggu Tagihan
"paid", // Belum Dibayar
"processed", // Diproses
"shipped", // Dikirim
"completed", // Selesai

View File

@ -234,8 +234,8 @@ class DetailOrderStatusActivity : AppCompatActivity() {
// Set status header
val statusText = when(status) {
"pending" -> "Belum Bayar"
"unpaid" -> "Belum Bayar"
"paid" -> "Sudah Dibayar"
"processed" -> "Diproses"
"shipped" -> "Dikirim"
"delivered" -> "Diterima"
@ -248,22 +248,6 @@ class DetailOrderStatusActivity : AppCompatActivity() {
Log.d(TAG, "adjustButtonsBasedOnStatus: Status header set to '$statusText'")
when (status) {
"pending"->{
binding.tvStatusHeader.text = "Menunggu Tagihan"
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")
showCancelOrderBottomSheet(orders.orderId)
viewModel.getOrderDetails(orders.orderId)
}
}
}
"unpaid" -> {
Log.d(TAG, "adjustButtonsBasedOnStatus: Setting up UI for pending/unpaid order")
@ -295,7 +279,25 @@ class DetailOrderStatusActivity : AppCompatActivity() {
}
}
}
"paid" -> {
Log.d(TAG, "adjustButtonsBasedOnStatus: Setting up UI for pending/unpaid order")
// Show status note
binding.tvStatusHeader.text = "Sudah Dibayar"
binding.tvStatusNote.visibility = View.VISIBLE
binding.tvStatusNote.text = "Menunggu pesanan dikonfirmasi penjual ${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())
viewModel.getOrderDetails(orders.orderId)
}
}
}
"processed" -> {
Log.d(TAG, "adjustButtonsBasedOnStatus: Setting up UI for processed order")

View File

@ -11,7 +11,7 @@ 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.GridLayoutManager
import com.alya.ecommerce_serang.BuildConfig.BASE_URL
import com.alya.ecommerce_serang.R
import com.alya.ecommerce_serang.data.api.dto.ProductsItem
@ -169,11 +169,7 @@ class StoreDetailActivity : AppCompatActivity() {
binding.rvProducts.apply {
adapter = productAdapter
layoutManager = LinearLayoutManager(
context,
LinearLayoutManager.HORIZONTAL,
false
)
layoutManager = GridLayoutManager(context, 2)
}
}

View File

@ -158,14 +158,13 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_products"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="0dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:orientation="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:layout_constraintTop_toBottomOf="@id/divider_product"
tools:itemCount="5"
tools:listitem="@layout/item_section_horizontal" />
app:spanCount="2"
tools:listitem="@layout/item_product_grid" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -15,34 +15,25 @@
android:layout_marginTop="16dp"
app:layout_constraintTop_toTopOf="parent" />
<!-- Home content in ScrollView -->
<ScrollView
<androidx.core.widget.NestedScrollView
android:id="@+id/home"
android:layout_width="match_parent"
android:layout_height="0dp"
android:fillViewport="true"
android:overScrollMode="never"
app:layout_constraintTop_toBottomOf="@id/searchContainer"
app:layout_constraintBottom_toBottomOf="parent">
<!-- Your existing home content here -->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- <androidx.viewpager2.widget.ViewPager2-->
<!-- android:id="@+id/banners"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="132dp"-->
<!-- android:layout_marginTop="8dp"-->
<!-- android:background="@drawable/banner_default"-->
<!-- android:orientation="vertical"-->
<!-- app:layout_constraintTop_toTopOf="parent"-->
<!-- tools:layout_editor_absoluteX="0dp" />-->
android:layout_height="wrap_content"
android:paddingBottom="16dp">
<TextView
android:id="@+id/categoriesText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="@string/fragment_home_categories"
android:textColor="@color/blue_500"
@ -56,7 +47,7 @@
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="32dp"
android:layout_marginEnd="16dp"
android:text="@string/show_all"
android:textAllCaps="false"
android:textColor="@color/blue_600"
@ -64,28 +55,28 @@
app:layout_constraintBaseline_toBaselineOf="@id/categoriesText"
app:layout_constraintEnd_toEndOf="parent" />
<!-- Fix margin by adding padding inside RecyclerView -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/categories"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="8dp"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="horizontal"
android:layout_marginHorizontal="16dp"
app:spanCount="2"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
android:overScrollMode="never"
android:nestedScrollingEnabled="false"
app:layout_constraintTop_toBottomOf="@id/categoriesText"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
tools:layout_editor_absoluteX="0dp"
tools:listitem="@layout/item_category_home" />
<TextView
android:id="@+id/new_products_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="@string/sold_product_text"
android:textColor="@color/blue_500"
@ -93,12 +84,13 @@
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/categories" />
<com.google.android.material.button.MaterialButton
android:id="@+id/productshowAll"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="32dp"
android:layout_marginEnd="16dp"
android:text="@string/show_all"
android:textAllCaps="false"
android:textColor="@color/blue_600"
@ -106,19 +98,22 @@
app:layout_constraintBaseline_toBaselineOf="@id/new_products_text"
app:layout_constraintEnd_toEndOf="parent" />
<!-- Disable internal scroll, height wraps content -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/new_products"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:orientation="vertical"
android:paddingHorizontal="16dp"
android:nestedScrollingEnabled="false"
android:overScrollMode="never"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="2"
app:layout_constraintTop_toBottomOf="@id/new_products_text"
tools:itemCount="5"
tools:listitem="@layout/item_section_horizontal" />
tools:listitem="@layout/item_product_grid" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</androidx.core.widget.NestedScrollView>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/searchResultsRecyclerView"

View File

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="110dp"
android:layout_height="115dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.cardview.widget.CardView

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="180dp"
android:layout_height="wrap_content"
android:layout_height="320dp"
android:layout_margin="8dp"
app:cardCornerRadius="12dp"
app:cardElevation="4dp"

View File

@ -81,7 +81,7 @@
<string name="dl_pending">Batas Tagihan</string>
<string name="dl_unpaid">Batas Pembayaran</string>
<string name="dl_processed">Batas Pengiriman</string>
<string name="dal_paid">Semua Pesanan </string>
<string name="dl_paid">Batas Konfirmasi Penjual</string>
<string name="dl_delivered">Semua Pesanan </string>
<string name="dl_completed">Semua Pesanan </string>
<string name="dl_shipped">Tanggal Pesanan Sampai</string>