add history and evidence

This commit is contained in:
shaulascr
2025-04-14 04:26:53 +07:00
parent 3051732b1e
commit 3bcee3b16c
26 changed files with 1497 additions and 88 deletions

View File

@ -0,0 +1,15 @@
package com.alya.ecommerce_serang.data.api.dto
import com.google.gson.annotations.SerializedName
import retrofit2.http.Multipart
data class AddEvidenceRequest (
@SerializedName("orer_id")
val orderId : Int,
@SerializedName("amount")
val amount : String,
@SerializedName("evidence")
val evidence: Multipart
)

View File

@ -0,0 +1,33 @@
package com.alya.ecommerce_serang.data.api.response.order
import com.google.gson.annotations.SerializedName
data class AddEvidenceResponse(
@field:SerializedName("evidence")
val evidence: Evidence,
@field:SerializedName("message")
val message: String
)
data class Evidence(
@field:SerializedName("amount")
val amount: String,
@field:SerializedName("evidence")
val evidence: String,
@field:SerializedName("uploaded_at")
val uploadedAt: String,
@field:SerializedName("id")
val id: Int,
@field:SerializedName("order_id")
val orderId: Int,
@field:SerializedName("status")
val status: String
)

View File

@ -14,22 +14,22 @@ data class OrderDetailResponse(
data class Orders(
@field:SerializedName("receipt_num")
val receiptNum: String,
val receiptNum: String? = null,
@field:SerializedName("payment_upload_at")
val paymentUploadAt: String,
val paymentUploadAt: String? = null,
@field:SerializedName("latitude")
val latitude: String,
@field:SerializedName("pay_info_name")
val payInfoName: String,
val payInfoName: String? = null,
@field:SerializedName("created_at")
val createdAt: String,
@field:SerializedName("voucher_code")
val voucherCode: Any,
val voucherCode: String? = null,
@field:SerializedName("updated_at")
val updatedAt: String,
@ -41,10 +41,10 @@ data class Orders(
val street: String,
@field:SerializedName("cancel_date")
val cancelDate: String,
val cancelDate: String? = null,
@field:SerializedName("payment_evidence")
val paymentEvidence: String,
val paymentEvidence: String? = null,
@field:SerializedName("longitude")
val longitude: String,
@ -53,34 +53,34 @@ data class Orders(
val shipmentStatus: String,
@field:SerializedName("order_items")
val orderItems: List<OrderItemsItem>,
val orderItems: List<OrderListItemsItem>,
@field:SerializedName("auto_completed_at")
val autoCompletedAt: String,
val autoCompletedAt: String? = null,
@field:SerializedName("is_store_location")
val isStoreLocation: Boolean,
val isStoreLocation: Boolean? = null,
@field:SerializedName("qris_image")
val qrisImage: String,
val qrisImage: String? = null,
@field:SerializedName("voucher_name")
val voucherName: Any,
val voucherName: String? = null,
@field:SerializedName("payment_status")
val paymentStatus: String,
val paymentStatus: String? = null,
@field:SerializedName("address_id")
val addressId: Int,
@field:SerializedName("payment_amount")
val paymentAmount: String,
val paymentAmount: String? = null,
@field:SerializedName("cancel_reason")
val cancelReason: String,
val cancelReason: String? = null,
@field:SerializedName("total_amount")
val totalAmount: String,
val totalAmount: String? = null,
@field:SerializedName("user_id")
val userId: Int,
@ -98,16 +98,16 @@ data class Orders(
val service: String,
@field:SerializedName("pay_info_num")
val payInfoNum: String,
val payInfoNum: String? = null,
@field:SerializedName("shipment_price")
val shipmentPrice: String,
@field:SerializedName("voucher_id")
val voucherId: Any,
val voucherId: Int? = null,
@field:SerializedName("payment_info_id")
val paymentInfoId: Int,
val paymentInfoId: Int? = null,
@field:SerializedName("detail")
val detail: String,
@ -122,13 +122,13 @@ data class Orders(
val cityId: Int
)
data class OrderItemsItem(
data class OrderListItemsItem(
@field:SerializedName("order_item_id")
val orderItemId: Int,
@field:SerializedName("review_id")
val reviewId: Int,
val reviewId: Int? = null,
@field:SerializedName("quantity")
val quantity: Int,

View File

@ -11,81 +11,119 @@ data class OrderListResponse(
val message: String
)
data class OrdersItem(
data class OrderItemsItem(
@field:SerializedName("receipt_num")
val receiptNum: String,
@field:SerializedName("review_id")
val reviewId: Int? = null,
@field:SerializedName("latitude")
val latitude: String,
@field:SerializedName("quantity")
val quantity: Int,
@field:SerializedName("created_at")
val createdAt: String,
@field:SerializedName("price")
val price: Int,
@field:SerializedName("voucher_code")
val voucherCode: String? = null,
@field:SerializedName("subtotal")
val subtotal: Int,
@field:SerializedName("updated_at")
val updatedAt: String,
@field:SerializedName("product_image")
val productImage: String,
@field:SerializedName("street")
val street: String,
@field:SerializedName("store_name")
val storeName: String,
@field:SerializedName("longitude")
val longitude: String,
@field:SerializedName("product_price")
val productPrice: Int,
@field:SerializedName("shipment_status")
val shipmentStatus: String,
@field:SerializedName("order_items")
val orderItems: List<OrderItemsItem>,
@field:SerializedName("is_store_location")
val isStoreLocation: Boolean,
@field:SerializedName("voucher_name")
val voucherName: String? = null,
@field:SerializedName("address_id")
val addressId: Int,
@field:SerializedName("payment_method_id")
val paymentMethodId: Int,
@field:SerializedName("total_amount")
val totalAmount: String,
@field:SerializedName("user_id")
val userId: Int,
@field:SerializedName("province_id")
val provinceId: Int,
@field:SerializedName("courier")
val courier: String,
@field:SerializedName("subdistrict")
val subdistrict: String,
@field:SerializedName("service")
val service: String,
@field:SerializedName("shipment_price")
val shipmentPrice: String,
@field:SerializedName("voucher_id")
val voucherId: Int? = null,
@field:SerializedName("detail")
val detail: String,
@field:SerializedName("postal_code")
val postalCode: String,
@field:SerializedName("order_id")
val orderId: Int,
@field:SerializedName("city_id")
val cityId: Int
@field:SerializedName("product_name")
val productName: String
)
data class OrdersItem(
@field:SerializedName("receipt_num")
val receiptNum: Int? = null,
@field:SerializedName("latitude")
val latitude: String,
@field:SerializedName("created_at")
val createdAt: String,
@field:SerializedName("voucher_code")
val voucherCode: String? = null,
@field:SerializedName("updated_at")
val updatedAt: String,
@field:SerializedName("etd")
val etd: String,
@field:SerializedName("street")
val street: String,
@field:SerializedName("cancel_date")
val cancelDate: String? = null,
@field:SerializedName("longitude")
val longitude: String,
@field:SerializedName("shipment_status")
val shipmentStatus: String,
@field:SerializedName("order_items")
val orderItems: List<OrderItemsItem>,
@field:SerializedName("auto_completed_at")
val autoCompletedAt: String? = null,
@field:SerializedName("is_store_location")
val isStoreLocation: Boolean? = null,
@field:SerializedName("voucher_name")
val voucherName: String? = null,
@field:SerializedName("address_id")
val addressId: Int,
@field:SerializedName("cancel_reason")
val cancelReason: String? = null,
@field:SerializedName("total_amount")
val totalAmount: String,
@field:SerializedName("user_id")
val userId: Int,
@field:SerializedName("province_id")
val provinceId: Int,
@field:SerializedName("courier")
val courier: String,
@field:SerializedName("subdistrict")
val subdistrict: String,
@field:SerializedName("service")
val service: String,
@field:SerializedName("shipment_price")
val shipmentPrice: String,
@field:SerializedName("voucher_id")
val voucherId: Int? = null,
@field:SerializedName("payment_info_id")
val paymentInfoId: Int? = null,
@field:SerializedName("detail")
val detail: String,
@field:SerializedName("postal_code")
val postalCode: String,
@field:SerializedName("order_id")
val orderId: Int,
@field:SerializedName("city_id")
val cityId: Int
)

View File

@ -1,5 +1,6 @@
package com.alya.ecommerce_serang.data.api.retrofit
import com.alya.ecommerce_serang.data.api.dto.AddEvidenceRequest
import com.alya.ecommerce_serang.data.api.dto.CartItem
import com.alya.ecommerce_serang.data.api.dto.CourierCostRequest
import com.alya.ecommerce_serang.data.api.dto.CreateAddressRequest
@ -16,10 +17,13 @@ import com.alya.ecommerce_serang.data.api.response.auth.RegisterResponse
import com.alya.ecommerce_serang.data.api.response.cart.AddCartResponse
import com.alya.ecommerce_serang.data.api.response.cart.ListCartResponse
import com.alya.ecommerce_serang.data.api.response.cart.UpdateCartResponse
import com.alya.ecommerce_serang.data.api.response.order.AddEvidenceResponse
import com.alya.ecommerce_serang.data.api.response.order.CourierCostResponse
import com.alya.ecommerce_serang.data.api.response.order.CreateOrderResponse
import com.alya.ecommerce_serang.data.api.response.order.ListCityResponse
import com.alya.ecommerce_serang.data.api.response.order.ListProvinceResponse
import com.alya.ecommerce_serang.data.api.response.order.OrderDetailResponse
import com.alya.ecommerce_serang.data.api.response.order.OrderListResponse
import com.alya.ecommerce_serang.data.api.response.product.AllProductResponse
import com.alya.ecommerce_serang.data.api.response.product.CategoryResponse
import com.alya.ecommerce_serang.data.api.response.product.DetailStoreProductResponse
@ -85,6 +89,21 @@ interface ApiService {
@Body request: OrderRequest
): Response<CreateOrderResponse>
@GET("order/detail/{id}")
suspend fun getDetailOrder(
@Path("id") orderId: Int
): Response<OrderDetailResponse>
@POST("order/addevidence")
suspend fun addEvidence(
@Body request : AddEvidenceRequest,
): Response<AddEvidenceResponse>
@GET("order/{status}")
suspend fun getOrderList(
@Path("status") status: String
):Response<OrderListResponse>
@POST("order")
suspend fun postOrderBuyNow(
@Body request: OrderRequestBuy

View File

@ -1,16 +1,20 @@
package com.alya.ecommerce_serang.data.repository
import android.util.Log
import com.alya.ecommerce_serang.data.api.dto.AddEvidenceRequest
import com.alya.ecommerce_serang.data.api.dto.CourierCostRequest
import com.alya.ecommerce_serang.data.api.dto.CreateAddressRequest
import com.alya.ecommerce_serang.data.api.dto.OrderRequest
import com.alya.ecommerce_serang.data.api.dto.OrderRequestBuy
import com.alya.ecommerce_serang.data.api.dto.UserProfile
import com.alya.ecommerce_serang.data.api.response.cart.DataItem
import com.alya.ecommerce_serang.data.api.response.order.AddEvidenceResponse
import com.alya.ecommerce_serang.data.api.response.order.CourierCostResponse
import com.alya.ecommerce_serang.data.api.response.order.CreateOrderResponse
import com.alya.ecommerce_serang.data.api.response.order.ListCityResponse
import com.alya.ecommerce_serang.data.api.response.order.ListProvinceResponse
import com.alya.ecommerce_serang.data.api.response.order.OrderDetailResponse
import com.alya.ecommerce_serang.data.api.response.order.OrderListResponse
import com.alya.ecommerce_serang.data.api.response.product.ProductResponse
import com.alya.ecommerce_serang.data.api.response.product.StoreProduct
import com.alya.ecommerce_serang.data.api.response.product.StoreResponse
@ -232,4 +236,59 @@ class OrderRepository(private val apiService: ApiService) {
}
}
suspend fun getOrderDetails(orderId: Int): OrderDetailResponse? {
val response = apiService.getDetailOrder(orderId)
return if (response.isSuccessful) response.body() else null
}
suspend fun uploadPaymentProof(request : AddEvidenceRequest): Result<AddEvidenceResponse> {
return try {
Log.d("OrderRepository", "Add Evidence : $request")
val response = apiService.addEvidence(request)
if (response.isSuccessful) {
val addEvidenceResponse = response.body()
if (addEvidenceResponse != null) {
Log.d("OrderRepository", "Add Evidence successfully: ${addEvidenceResponse.message}")
Result.Success(addEvidenceResponse)
} else {
Log.e("OrderRepository", "Response body was null")
Result.Error(Exception("Empty response from server"))
}
} else {
val errorBody = response.errorBody()?.string() ?: "Unknown error"
Log.e("OrderRepository", "Error Add Evidence : $errorBody")
Result.Error(Exception(errorBody))
}
} catch (e: Exception) {
Log.e("OrderRepository", "Exception Add Evidence ", e)
Result.Error(e)
}
}
suspend fun getOrderList(status: String): Result<OrderListResponse> {
return try {
Log.d("OrderRepository", "Add Evidence : $status")
val response = apiService.getOrderList(status)
if (response.isSuccessful) {
val allListOrder = response.body()
if (allListOrder != null) {
Log.d("OrderRepository", "Add Evidence successfully: ${allListOrder.message}")
Result.Success(allListOrder)
} else {
Log.e("OrderRepository", "Response body was null")
Result.Error(Exception("Empty response from server"))
}
} else {
val errorBody = response.errorBody()?.string() ?: "Unknown error"
Log.e("OrderRepository", "Error Add Evidence : $errorBody")
Result.Error(Exception(errorBody))
}
} catch (e: Exception) {
Log.e("OrderRepository", "Exception Add Evidence ", e)
Result.Error(e)
}
}
}

View File

@ -0,0 +1,21 @@
package com.alya.ecommerce_serang.ui.order.detail
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.alya.ecommerce_serang.R
class PaymentActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_payment)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
}
}

View File

@ -0,0 +1,56 @@
package com.alya.ecommerce_serang.ui.order.history
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.commit
import com.alya.ecommerce_serang.R
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
import com.alya.ecommerce_serang.data.repository.OrderRepository
import com.alya.ecommerce_serang.databinding.ActivityHistoryBinding
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
import com.alya.ecommerce_serang.utils.SessionManager
class HistoryActivity : AppCompatActivity() {
private lateinit var binding: ActivityHistoryBinding
private lateinit var sessionManager: SessionManager
private val viewModel: HistoryViewModel by viewModels {
BaseViewModelFactory {
val apiService = ApiConfig.getApiService(sessionManager)
val orderRepository = OrderRepository(apiService)
HistoryViewModel(orderRepository)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHistoryBinding.inflate(layoutInflater)
setContentView(binding.root)
sessionManager = SessionManager(this)
setupToolbar()
if (savedInstanceState == null) {
showOrderFragment()
}
}
private fun setupToolbar() {
setSupportActionBar(binding.toolbar)
supportActionBar?.setDisplayShowTitleEnabled(false)
binding.btnBack.setOnClickListener {
onBackPressed()
}
}
private fun showOrderFragment() {
supportFragmentManager.commit {
replace(R.id.fragment_container_history, OrderHistoryFragment())
}
}
}

View File

@ -0,0 +1,48 @@
package com.alya.ecommerce_serang.ui.order.history
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.response.order.OrdersItem
import com.alya.ecommerce_serang.data.repository.OrderRepository
import com.alya.ecommerce_serang.data.repository.Result
import com.alya.ecommerce_serang.ui.order.address.ViewState
import kotlinx.coroutines.launch
class HistoryViewModel(private val repository: OrderRepository) : ViewModel() {
companion object {
private const val TAG = "HistoryViewModel"
}
private val _orders = MutableLiveData<ViewState<List<OrdersItem>>>()
val orders: LiveData<ViewState<List<OrdersItem>>> = _orders
fun getOrderList(status: String) {
_orders.value = ViewState.Loading
viewModelScope.launch {
_orders.value = ViewState.Loading
try {
when (val result = repository.getOrderList(status)) {
is Result.Success -> {
_orders.value = ViewState.Success(result.data.orders)
Log.d("HistoryViewModel", "Orders loaded successfully: ${result.data.orders.size} items")
}
is Result.Error -> {
_orders.value = ViewState.Error(result.exception.message ?: "Unknown error occurred")
Log.e("HistoryViewModel", "Error loading orders", result.exception)
}
is Result.Loading -> {
null
}
}
} catch (e: Exception) {
_orders.value = ViewState.Error("An unexpected error occurred: ${e.message}")
Log.e("HistoryViewModel", "Exception in getOrderList", e)
}
}
}
}

View File

@ -0,0 +1,111 @@
package com.alya.ecommerce_serang.ui.order.history
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.alya.ecommerce_serang.R
import com.alya.ecommerce_serang.data.api.response.order.OrdersItem
class OrderHistoryAdapter(
private val onOrderClickListener: (OrdersItem) -> Unit
) : RecyclerView.Adapter<OrderHistoryAdapter.OrderViewHolder>() {
private val orders = mutableListOf<OrdersItem>()
fun submitList(newOrders: List<OrdersItem>) {
orders.clear()
orders.addAll(newOrders)
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OrderViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_order_history, parent, false)
return OrderViewHolder(view)
}
override fun onBindViewHolder(holder: OrderViewHolder, position: Int) {
holder.bind(orders[position])
}
override fun getItemCount(): Int = orders.size
inner class OrderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val tvStoreName: TextView = itemView.findViewById(R.id.tvStoreName)
private val tvOrderStatus: TextView = itemView.findViewById(R.id.tvOrderStatus)
private val rvOrderItems: RecyclerView = itemView.findViewById(R.id.rvOrderItems)
private val tvShowMore: TextView = itemView.findViewById(R.id.tvShowMore)
private val tvTotalAmount: TextView = itemView.findViewById(R.id.tvTotalAmount)
private val tvItemCountLabel: TextView = itemView.findViewById(R.id.tv_count_total_item)
private val tvDeadlineDate: TextView = itemView.findViewById(R.id.tvDeadlineDate)
fun bind(order: OrdersItem) {
// Get store name from the first order item
val storeName = if (order.orderItems.isNotEmpty()) order.orderItems[0].storeName else ""
tvStoreName.text = storeName
// Set order status based on shipment status
tvOrderStatus.text = getStatusLabel(order.shipmentStatus)
// Set total amount
tvTotalAmount.text = order.totalAmount
// Set item count
val itemCount = order.orderItems.size
tvItemCountLabel.text = itemView.context.getString(R.string.item_count_prod, itemCount)
// Set deadline date
tvDeadlineDate.text = formatDate(order.createdAt) // This would need a proper date formatting function
// Set up the order items RecyclerView
val productAdapter = OrderProductAdapter()
rvOrderItems.apply {
layoutManager = LinearLayoutManager(itemView.context)
adapter = productAdapter
}
// Display only the first product and show "View more" for the rest
if (order.orderItems.isNotEmpty()) {
productAdapter.submitList(order.orderItems.take(1))
// Show or hide the "View more" text based on number of items
if (order.orderItems.size > 1) {
val itemString = order.orderItems.size - 1
tvShowMore.visibility = View.VISIBLE
tvShowMore.text = itemView.context.getString(R.string.show_more_product, itemString)
} else {
tvShowMore.visibility = View.GONE
}
} else {
tvShowMore.visibility = View.GONE
}
// Set click listener for the entire order item
itemView.setOnClickListener {
onOrderClickListener(order)
}
}
private fun getStatusLabel(status: String): String {
return when (status.toLowerCase()) {
"pending" -> itemView.context.getString(R.string.pending_orders)
"unpaid" -> itemView.context.getString(R.string.unpaid_orders)
"processed" -> itemView.context.getString(R.string.processed_orders)
"paid" -> itemView.context.getString(R.string.paid_orders)
"shipped" -> itemView.context.getString(R.string.shipped_orders)
"delivered" -> itemView.context.getString(R.string.delivered_orders)
"completed" -> itemView.context.getString(R.string.completed_orders)
"canceled" -> itemView.context.getString(R.string.canceled_orders)
else -> status
}
}
private fun formatDate(dateString: String): String {
// In a real app, you would parse the date string and format it
// For this example, just return the string as is
return dateString
}
}
}

View File

@ -0,0 +1,64 @@
package com.alya.ecommerce_serang.ui.order.history
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.alya.ecommerce_serang.R
import com.alya.ecommerce_serang.databinding.FragmentOrderHistoryBinding
import com.alya.ecommerce_serang.utils.SessionManager
import com.google.android.material.tabs.TabLayoutMediator
class OrderHistoryFragment : Fragment() {
private var _binding: FragmentOrderHistoryBinding? = null
private val binding get() = _binding!!
private lateinit var sessionManager: SessionManager
private lateinit var viewPagerAdapter: OrderViewPagerAdapter
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentOrderHistoryBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
sessionManager = SessionManager(requireContext())
setupViewPager()
}
private fun setupViewPager() {
// Initialize the ViewPager adapter
viewPagerAdapter = OrderViewPagerAdapter(requireActivity())
binding.viewPager.adapter = viewPagerAdapter
// Connect TabLayout with ViewPager2
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)
3 -> getString(R.string.processed_orders)
4 -> getString(R.string.paid_orders)
5 -> getString(R.string.shipped_orders)
6 -> getString(R.string.delivered_orders)
7 -> getString(R.string.completed_orders)
8 -> getString(R.string.canceled_orders)
else -> "Tab $position"
}
}.attach()
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}

View File

@ -0,0 +1,127 @@
package com.alya.ecommerce_serang.ui.order.history
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.LinearLayoutManager
import com.alya.ecommerce_serang.data.api.response.order.OrdersItem
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
import com.alya.ecommerce_serang.data.repository.OrderRepository
import com.alya.ecommerce_serang.databinding.FragmentOrderListBinding
import com.alya.ecommerce_serang.ui.order.address.ViewState
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
import com.alya.ecommerce_serang.utils.SessionManager
class OrderListFragment : Fragment() {
private var _binding: FragmentOrderListBinding? = null
private val binding get() = _binding!!
private lateinit var sessionManager: SessionManager
private val viewModel: HistoryViewModel by viewModels {
BaseViewModelFactory {
val apiService = ApiConfig.getApiService(sessionManager)
val orderRepository = OrderRepository(apiService)
HistoryViewModel(orderRepository)
}
}
private lateinit var orderAdapter: OrderHistoryAdapter
private var status: String = "all"
companion object {
private const val ARG_STATUS = "status"
fun newInstance(status: String): OrderListFragment {
return OrderListFragment().apply {
arguments = Bundle().apply {
putString(ARG_STATUS, status)
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sessionManager = SessionManager(requireContext())
arguments?.let {
status = it.getString(ARG_STATUS) ?: "all"
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentOrderListBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupRecyclerView()
observeOrderList()
loadOrders()
}
private fun setupRecyclerView() {
orderAdapter = OrderHistoryAdapter { order ->
// Handle order click
navigateToOrderDetail(order)
}
binding.rvOrders.apply {
layoutManager = LinearLayoutManager(requireContext())
adapter = orderAdapter
}
}
private fun observeOrderList() {
viewModel.orders.observe(viewLifecycleOwner) { result ->
when (result) {
is ViewState.Success -> {
binding.progressBar.visibility = View.GONE
if (result.data.isNullOrEmpty()) {
binding.tvEmptyState.visibility = View.VISIBLE
binding.rvOrders.visibility = View.GONE
} else {
binding.tvEmptyState.visibility = View.GONE
binding.rvOrders.visibility = View.VISIBLE
orderAdapter.submitList(result.data)
}
}
is ViewState.Error -> {
binding.progressBar.visibility = View.GONE
binding.tvEmptyState.visibility = View.VISIBLE
Toast.makeText(requireContext(), result.message, Toast.LENGTH_SHORT).show()
}
is ViewState.Loading -> {
null
}
}
}
}
private fun loadOrders() {
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()
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}

View File

@ -0,0 +1,64 @@
package com.alya.ecommerce_serang.ui.order.history
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.order.OrderItemsItem
import com.bumptech.glide.Glide
class OrderProductAdapter : RecyclerView.Adapter<OrderProductAdapter.ProductViewHolder>() {
private val products = mutableListOf<OrderItemsItem>()
fun submitList(newProducts: List<OrderItemsItem>) {
products.clear()
products.addAll(newProducts)
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_order_product, parent, false)
return ProductViewHolder(view)
}
override fun onBindViewHolder(holder: ProductViewHolder, position: Int) {
holder.bind(products[position])
}
override fun getItemCount(): Int = products.size
inner class ProductViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val ivProductImage: ImageView = itemView.findViewById(R.id.iv_product)
private val tvProductName: TextView = itemView.findViewById(R.id.tv_product_name)
private val tvQuantity: TextView = itemView.findViewById(R.id.tv_product_quantity)
private val tvProductPrice: TextView = itemView.findViewById(R.id.tv_product_price)
fun bind(product: OrderItemsItem) {
// Set product name
tvProductName.text = product.productName
// Set quantity with suffix
tvQuantity.text = "${product.quantity} buah"
// Set price with currency format
tvProductPrice.text = formatCurrency(product.price)
// Load product image using Glide
Glide.with(itemView.context)
.load(product.productImage)
.placeholder(R.drawable.placeholder_image)
// .error(R.drawable.error_image)
.into(ivProductImage)
}
private fun formatCurrency(amount: Int): String {
// In a real app, you would use NumberFormat for proper currency formatting
// For simplicity, just return a basic formatted string
return "Rp${amount}"
}
}
}

View File

@ -0,0 +1,30 @@
package com.alya.ecommerce_serang.ui.order.history
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
class OrderViewPagerAdapter(
fragmentActivity: FragmentActivity
) : FragmentStateAdapter(fragmentActivity) {
// Define all possible order statuses
private val orderStatuses = listOf(
"all", // All orders
"pending", // Menunggu Tagihan
"unpaid", // Belum Dibayar
"processed", // Diproses
"paid", // Dibayar
"shipped", // Dikirim
"delivered", // Diterima
"completed", // Selesai
"canceled" // Dibatalkan
)
override fun getItemCount(): Int = orderStatuses.size
override fun createFragment(position: Int): Fragment {
// Create a new instance of OrderListFragment with the appropriate status
return OrderListFragment.newInstance(orderStatuses[position])
}
}

View File

@ -15,6 +15,7 @@ import com.alya.ecommerce_serang.data.api.dto.UserProfile
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
import com.alya.ecommerce_serang.data.repository.UserRepository
import com.alya.ecommerce_serang.databinding.FragmentProfileBinding
import com.alya.ecommerce_serang.ui.order.history.HistoryActivity
import com.alya.ecommerce_serang.ui.profile.mystore.MyStoreActivity
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
import com.alya.ecommerce_serang.utils.SessionManager
@ -63,6 +64,16 @@ class ProfileFragment : Fragment() {
val intentDetail = Intent(requireContext(), DetailProfileActivity::class.java)
startActivity(intentDetail)
}
binding.tvLihatRiwayat.setOnClickListener{
val intent = Intent(requireContext(), HistoryActivity::class.java)
startActivity(intent)
}
binding.cardPesanan.setOnClickListener{
val intent = Intent(requireContext(), HistoryActivity::class.java)
startActivity(intent)
}
}
private fun observeUserProfile() {