diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c1cfce9..e47879a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -19,6 +19,12 @@ android:theme="@style/Theme.Ecommerce_serang" android:usesCleartextTraffic="true" tools:targetApi="31"> + + diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/AddEvidenceRequest.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/AddEvidenceRequest.kt new file mode 100644 index 0000000..baee00c --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/AddEvidenceRequest.kt @@ -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 +) \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/order/AddEvidenceResponse.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/order/AddEvidenceResponse.kt new file mode 100644 index 0000000..4fe220e --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/order/AddEvidenceResponse.kt @@ -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 +) diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/order/OrderDetailResponse.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/order/OrderDetailResponse.kt index 20b6432..2e75e98 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/order/OrderDetailResponse.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/order/OrderDetailResponse.kt @@ -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, + val orderItems: List, @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, diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/order/OrderListResponse.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/order/OrderListResponse.kt index a962817..069d97d 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/order/OrderListResponse.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/order/OrderListResponse.kt @@ -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, - - @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, + + @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 +) diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt index 2367f28..e916206 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt @@ -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 + @GET("order/detail/{id}") + suspend fun getDetailOrder( + @Path("id") orderId: Int + ): Response + + @POST("order/addevidence") + suspend fun addEvidence( + @Body request : AddEvidenceRequest, + ): Response + + @GET("order/{status}") + suspend fun getOrderList( + @Path("status") status: String + ):Response + @POST("order") suspend fun postOrderBuyNow( @Body request: OrderRequestBuy diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/repository/OrderRepository.kt b/app/src/main/java/com/alya/ecommerce_serang/data/repository/OrderRepository.kt index b4bc29a..36942f1 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/data/repository/OrderRepository.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/data/repository/OrderRepository.kt @@ -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 { + 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 { + 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) + } + } + } \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/order/detail/PaymentActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/order/detail/PaymentActivity.kt new file mode 100644 index 0000000..1ba879a --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/order/detail/PaymentActivity.kt @@ -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 + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/order/history/HistoryActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/order/history/HistoryActivity.kt new file mode 100644 index 0000000..fdc7f1d --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/order/history/HistoryActivity.kt @@ -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()) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/order/history/HistoryViewModel.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/order/history/HistoryViewModel.kt new file mode 100644 index 0000000..7f65ae1 --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/order/history/HistoryViewModel.kt @@ -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>>() + val orders: LiveData>> = _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) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/order/history/OrderHistoryAdapter.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/order/history/OrderHistoryAdapter.kt new file mode 100644 index 0000000..b1b13f4 --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/order/history/OrderHistoryAdapter.kt @@ -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() { + + private val orders = mutableListOf() + + fun submitList(newOrders: List) { + 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 + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/order/history/OrderHistoryFragment.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/order/history/OrderHistoryFragment.kt new file mode 100644 index 0000000..b7d0f6e --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/order/history/OrderHistoryFragment.kt @@ -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 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/order/history/OrderListFragment.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/order/history/OrderListFragment.kt new file mode 100644 index 0000000..6686484 --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/order/history/OrderListFragment.kt @@ -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 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/order/history/OrderProductAdapter.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/order/history/OrderProductAdapter.kt new file mode 100644 index 0000000..2cfcb16 --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/order/history/OrderProductAdapter.kt @@ -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() { + + private val products = mutableListOf() + + fun submitList(newProducts: List) { + 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}" + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/order/history/OrderViewPageAdapter.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/order/history/OrderViewPageAdapter.kt new file mode 100644 index 0000000..a759240 --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/order/history/OrderViewPageAdapter.kt @@ -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]) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/ProfileFragment.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/ProfileFragment.kt index 1465f3f..b527fd9 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/ProfileFragment.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/ProfileFragment.kt @@ -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() { diff --git a/app/src/main/res/drawable/bg_button_filled.xml b/app/src/main/res/drawable/bg_button_filled.xml new file mode 100644 index 0000000..967a6d9 --- /dev/null +++ b/app/src/main/res/drawable/bg_button_filled.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_button_outline.xml b/app/src/main/res/drawable/bg_button_outline.xml new file mode 100644 index 0000000..36bcbbf --- /dev/null +++ b/app/src/main/res/drawable/bg_button_outline.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/app/src/main/res/layout/activity_history.xml b/app/src/main/res/layout/activity_history.xml new file mode 100644 index 0000000..bfa8178 --- /dev/null +++ b/app/src/main/res/layout/activity_history.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_payment.xml b/app/src/main/res/layout/activity_payment.xml new file mode 100644 index 0000000..731356b --- /dev/null +++ b/app/src/main/res/layout/activity_payment.xml @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +