diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index cab34b5..3c3dfbd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -29,6 +29,9 @@
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/ReviewProductItem.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/ReviewProductItem.kt
index cfe8a65..9d9da34 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/ReviewProductItem.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/ReviewProductItem.kt
@@ -10,5 +10,13 @@ data class ReviewProductItem (
val rating : Int,
@SerializedName("review_text")
- val reviewTxt : String
+ val reviewTxt : String = ""
+)
+
+data class ReviewUIItem(
+ val orderItemId: Int,
+ val productName: String,
+ val productImage: String,
+ var rating: Int = 5, // Default rating is 5 stars
+ var reviewText: String = "" // Empty by default, to be filled by user
)
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/customer/product/DetailStoreProductResponse.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/customer/product/DetailStoreProductResponse.kt
index fa02d06..0e2015f 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/customer/product/DetailStoreProductResponse.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/customer/product/DetailStoreProductResponse.kt
@@ -4,6 +4,12 @@ import com.google.gson.annotations.SerializedName
data class DetailStoreProductResponse(
+ @field:SerializedName("shipping")
+ val shipping: List,
+
+ @field:SerializedName("payment")
+ val payment: List,
+
@field:SerializedName("store")
val store: StoreProduct,
@@ -11,28 +17,11 @@ data class DetailStoreProductResponse(
val message: String
)
-data class PaymentInfoItem(
-
- val id: Int = 1,
-
- @field:SerializedName("qris_image")
- val qrisImage: String? = null,
-
- @field:SerializedName("bank_num")
- val bankNum: String,
-
- @field:SerializedName("name")
- val name: String
-)
-
data class StoreProduct(
@field:SerializedName("store_id")
val storeId: Int,
- @field:SerializedName("shipping_service")
- val shippingService: List,
-
@field:SerializedName("store_rating")
val storeRating: String,
@@ -45,21 +34,36 @@ data class StoreProduct(
@field:SerializedName("store_type")
val storeType: String,
- @field:SerializedName("payment_info")
- val paymentInfo: List,
-
@field:SerializedName("store_location")
val storeLocation: String,
@field:SerializedName("store_image")
- val storeImage: String,
+ val storeImage: String? = null,
@field:SerializedName("status")
val status: String
)
-data class ShippingServiceItem(
+data class ShippingItemDetail(
@field:SerializedName("courier")
val courier: String
)
+
+data class PaymentItemDetail(
+
+ @field:SerializedName("qris_image")
+ val qrisImage: String,
+
+ @field:SerializedName("bank_num")
+ val bankNum: String,
+
+ @field:SerializedName("account_name")
+ val accountName: Any,
+
+ @field:SerializedName("bank_name")
+ val bankName: String,
+
+ @field:SerializedName("id")
+ val id: Int
+)
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 5af3dac..835035c 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
@@ -8,15 +8,18 @@ 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.OrdersItem
+import com.alya.ecommerce_serang.data.api.dto.ReviewProductItem
import com.alya.ecommerce_serang.data.api.dto.UpdateCart
import com.alya.ecommerce_serang.data.api.dto.UserProfile
import com.alya.ecommerce_serang.data.api.response.customer.cart.DataItemCart
import com.alya.ecommerce_serang.data.api.response.customer.order.CourierCostResponse
import com.alya.ecommerce_serang.data.api.response.customer.order.CreateOrderResponse
+import com.alya.ecommerce_serang.data.api.response.customer.order.CreateReviewResponse
import com.alya.ecommerce_serang.data.api.response.customer.order.ListCityResponse
import com.alya.ecommerce_serang.data.api.response.customer.order.ListProvinceResponse
import com.alya.ecommerce_serang.data.api.response.customer.order.OrderDetailResponse
import com.alya.ecommerce_serang.data.api.response.customer.order.OrderListResponse
+import com.alya.ecommerce_serang.data.api.response.customer.product.PaymentItemDetail
import com.alya.ecommerce_serang.data.api.response.customer.product.ProductResponse
import com.alya.ecommerce_serang.data.api.response.customer.product.StoreProduct
import com.alya.ecommerce_serang.data.api.response.customer.product.StoreResponse
@@ -179,8 +182,6 @@ class OrderRepository(private val apiService: ApiService) {
}
}
-
-
suspend fun updateCart(updateCart: UpdateCart): Result {
return try {
val response = apiService.updateCart(updateCart)
@@ -236,6 +237,27 @@ class OrderRepository(private val apiService: ApiService) {
}
}
+ suspend fun fetchPaymentStore(storeId: Int): Result> {
+ return try {
+ val response = apiService.getDetailStore(storeId)
+ if (response.isSuccessful) {
+ val store = response.body()?.payment
+ if (store != null) {
+ Result.Success(store)
+ } else {
+ Result.Error(Exception("Payment details not found"))
+ }
+ } else {
+ val errorMsg = response.errorBody()?.string() ?: "Unknown error"
+ Log.e("Order Repository", "Error fetching store: $errorMsg")
+ Result.Error(Exception(errorMsg))
+ }
+ } catch (e: Exception) {
+ Log.e("Order Repository", "Exception fetching payment details", e)
+ Result.Error(e)
+ }
+ }
+
suspend fun addAddress(request: CreateAddressRequest): Result {
return try {
Log.d("OrderRepository", "Adding address: $request")
@@ -475,4 +497,27 @@ class OrderRepository(private val apiService: ApiService) {
}
}.flowOn(Dispatchers.IO)
+ suspend fun createReviewProduct(review: ReviewProductItem): Result{
+ return try{
+ Log.d("Order Repository", "Sending review item product: $review")
+ val response = apiService.createReview(review)
+
+ if (response.isSuccessful){
+ response.body()?.let { reviewProductResponse ->
+ Log.d("Order Repository", " Successful create review. Review item rating: ${reviewProductResponse.rating}, orderItemId: ${reviewProductResponse.orderItemId}")
+ Result.Success(reviewProductResponse)
+ } ?: run {
+ Result.Error(Exception("Failed to create review"))
+ }
+ } else {
+ val errorMsg = response.errorBody()?.string() ?: "Unknown Error"
+ Log.e("Order Repository", "Error create review. Code ${response.code()}, Error: $errorMsg")
+ Result.Error(Exception(errorMsg))
+ }
+ } catch (e:Exception) {
+ Result.Error(e)
+ }
+
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/order/CheckoutActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/order/CheckoutActivity.kt
index 88dd5f6..b8d9c36 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/ui/order/CheckoutActivity.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/order/CheckoutActivity.kt
@@ -15,7 +15,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.alya.ecommerce_serang.data.api.dto.CheckoutData
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.response.customer.product.PaymentInfoItem
+import com.alya.ecommerce_serang.data.api.response.customer.product.PaymentItemDetail
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
import com.alya.ecommerce_serang.data.repository.OrderRepository
import com.alya.ecommerce_serang.databinding.ActivityCheckoutBinding
@@ -47,7 +47,6 @@ class CheckoutActivity : AppCompatActivity() {
sessionManager = SessionManager(this)
-
// Setup UI components
setupToolbar()
setupObservers()
@@ -100,9 +99,7 @@ class CheckoutActivity : AppCompatActivity() {
updateOrderSummary()
if (data != null) {
- viewModel.getPaymentMethods { paymentMethods ->
- Log.d("CheckoutActivity", "Loaded ${paymentMethods.size} payment methods")
- }
+ viewModel.getPaymentMethods()
}
}
@@ -122,7 +119,7 @@ class CheckoutActivity : AppCompatActivity() {
viewModel.selectedPayment.observe(this) { selectedPayment ->
if (selectedPayment != null) {
- Log.d("CheckoutActivity", "Observer notified of selected payment: ${selectedPayment.name}")
+ Log.d("CheckoutActivity", "Observer notified of selected payment: ${selectedPayment.bankName}")
// Update the adapter ONLY if it exists
paymentAdapter?.let { adapter ->
@@ -157,7 +154,7 @@ class CheckoutActivity : AppCompatActivity() {
}
}
- private fun setupPaymentMethodsRecyclerView(paymentMethods: List) {
+ private fun setupPaymentMethodsRecyclerView(paymentMethods: List) {
if (paymentMethods.isEmpty()) {
Log.e("CheckoutActivity", "Payment methods list is empty")
Toast.makeText(this, "No payment methods available", Toast.LENGTH_SHORT).show()
@@ -169,7 +166,7 @@ class CheckoutActivity : AppCompatActivity() {
if (paymentAdapter == null) {
paymentAdapter = PaymentMethodAdapter(paymentMethods) { payment ->
- Log.d("CheckoutActivity", "Payment selected in adapter: ${payment.name}")
+ Log.d("CheckoutActivity", "Payment selected in adapter: ${payment.bankName}")
// Set this payment as selected in the ViewModel
viewModel.setPaymentMethod(payment.id)
@@ -182,7 +179,7 @@ class CheckoutActivity : AppCompatActivity() {
}
}
- private fun updatePaymentMethodsAdapter(paymentMethods: List, selectedId: Int?) {
+ private fun updatePaymentMethodsAdapter(paymentMethods: List, selectedId: Int?) {
Log.d("CheckoutActivity", "Updating payment adapter with ${paymentMethods.size} methods")
// Simple test adapter
@@ -198,7 +195,7 @@ class CheckoutActivity : AppCompatActivity() {
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val payment = paymentMethods[position]
- (holder.itemView as TextView).text = "Payment: ${payment.name}"
+ (holder.itemView as TextView).text = "Payment: ${payment.bankName}"
}
}
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/order/CheckoutViewModel.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/order/CheckoutViewModel.kt
index cba73c1..429baac 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/ui/order/CheckoutViewModel.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/order/CheckoutViewModel.kt
@@ -10,7 +10,7 @@ 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.response.customer.cart.CartItemsItem
import com.alya.ecommerce_serang.data.api.response.customer.cart.DataItemCart
-import com.alya.ecommerce_serang.data.api.response.customer.product.PaymentInfoItem
+import com.alya.ecommerce_serang.data.api.response.customer.product.PaymentItemDetail
import com.alya.ecommerce_serang.data.api.response.customer.profile.AddressesItem
import com.alya.ecommerce_serang.data.repository.OrderRepository
import com.alya.ecommerce_serang.data.repository.Result
@@ -24,12 +24,12 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
private val _addressDetails = MutableLiveData()
val addressDetails: LiveData = _addressDetails
- private val _availablePaymentMethods = MutableLiveData>()
- val availablePaymentMethods: LiveData> = _availablePaymentMethods
+ private val _availablePaymentMethods = MutableLiveData>()
+ val availablePaymentMethods: LiveData> = _availablePaymentMethods
// Selected payment method
- private val _selectedPayment = MutableLiveData()
- val selectedPayment: LiveData = _selectedPayment
+ private val _selectedPayment = MutableLiveData()
+ val selectedPayment: LiveData = _selectedPayment
private val _isLoading = MutableLiveData()
val isLoading: LiveData = _isLoading
@@ -156,48 +156,45 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
}
}
- fun getPaymentMethods(callback: (List) -> Unit) {
+ fun getPaymentMethods() {
viewModelScope.launch {
try {
- val storeId = _checkoutData.value?.sellerId ?: return@launch
-
- Log.d(TAG, "Attempting to fetch payment methods for storeId: $storeId")
-
- if (storeId == null || storeId <= 0) {
- Log.e(TAG, "Invalid storeId: $storeId - cannot fetch payment methods")
+ val storeId = _checkoutData.value?.sellerId ?: run {
+ Log.e(TAG, "StoreId is null - cannot fetch payment methods")
+ _availablePaymentMethods.value = emptyList()
return@launch
}
- // Use fetchStoreDetail instead of getStore
- val storeResult = repository.fetchStoreDetail(storeId)
+ Log.d(TAG, "Attempting to fetch payment methods for storeId: $storeId")
- if (storeResult is Result.Success && storeResult.data != null) {
- // For now, we'll use hardcoded payment ID (1) for all payment methods
- // This will be updated once the backend provides proper IDs
- val paymentMethodsList = storeResult.data.paymentInfo.map { paymentInfo ->
- PaymentInfoItem(
- id = paymentInfo.id ?: 1,
- name = paymentInfo.name,
- bankNum = paymentInfo.bankNum,
- qrisImage = paymentInfo.qrisImage
- )
- }
-
- Log.d(TAG, "Fetched ${paymentMethodsList.size} payment methods")
-
- // Only update if we don't already have payment methods
- if (_availablePaymentMethods.value.isNullOrEmpty()) {
- _availablePaymentMethods.value = paymentMethodsList
- }
- callback(paymentMethodsList)
- } else {
+ if (storeId <= 0) {
+ Log.e(TAG, "Invalid storeId: $storeId - cannot fetch payment methods")
_availablePaymentMethods.value = emptyList()
- callback(emptyList())
+ return@launch
+ }
+
+ val result = repository.fetchPaymentStore(storeId)
+
+ when (result) {
+ is Result.Success -> {
+ val paymentMethods = result.data?.filterNotNull() ?: emptyList()
+
+ Log.d(TAG, "Fetched ${paymentMethods.size} payment methods")
+
+ // Update payment methods
+ _availablePaymentMethods.value = paymentMethods
+ }
+ is Result.Error -> {
+ Log.e(TAG, "Error fetching payment methods: ${result.exception.message}")
+ _availablePaymentMethods.value = emptyList()
+ }
+ is Result.Loading -> {
+ null
+ }
}
} catch (e: Exception) {
- Log.e(TAG, "Error fetching payment methods", e)
+ Log.e(TAG, "Exception in getPaymentMethods", e)
_availablePaymentMethods.value = emptyList()
- callback(emptyList())
}
}
}
@@ -211,7 +208,7 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
if (paymentMethods.isNullOrEmpty()) {
// If no payment methods available, try to fetch them
- getPaymentMethods { /* do nothing here */ }
+ getPaymentMethods()
return@launch
}
@@ -224,7 +221,7 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
// Set the selected payment - IMPORTANT: do this first
_selectedPayment.value = selectedPayment
- Log.d(TAG, "Payment selected: ID=${selectedPayment.id}, Name=${selectedPayment.name}")
+ Log.d(TAG, "Payment selected: ID=${selectedPayment.id}, Name=${selectedPayment.bankName}")
// Update the order request with the payment method ID
val currentData = _checkoutData.value ?: return@launch
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/order/PaymentMethodAdapter.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/order/PaymentMethodAdapter.kt
index dd2a20e..6028406 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/ui/order/PaymentMethodAdapter.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/order/PaymentMethodAdapter.kt
@@ -6,14 +6,14 @@ import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.alya.ecommerce_serang.R
-import com.alya.ecommerce_serang.data.api.response.customer.product.PaymentInfoItem
+import com.alya.ecommerce_serang.data.api.response.customer.product.PaymentItemDetail
import com.alya.ecommerce_serang.databinding.ItemPaymentMethodBinding
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
class PaymentMethodAdapter(
- private val paymentMethods: List,
- private val onPaymentSelected: (PaymentInfoItem) -> Unit
+ private val paymentMethods: List,
+ private val onPaymentSelected: (PaymentItemDetail) -> Unit
) : RecyclerView.Adapter() {
// Track the selected payment by ID
@@ -38,13 +38,13 @@ class PaymentMethodAdapter(
with(holder.binding) {
// Set payment method name
- tvPaymentMethodName.text = payment.name
+ tvPaymentMethodName.text = payment.bankName
// Set radio button state based on selected payment ID
rbPaymentMethod.isChecked = payment.id == selectedPaymentId
// Debug log
- Log.d("PaymentAdapter", "Binding item ${payment.name}, checked=${rbPaymentMethod.isChecked}")
+ Log.d("PaymentAdapter", "Binding item ${payment.bankName}, checked=${rbPaymentMethod.isChecked}")
// Load payment icon if available
if (!payment.qrisImage.isNullOrEmpty()) {
@@ -84,7 +84,7 @@ class PaymentMethodAdapter(
// Call the callback ONLY ONCE
onPaymentSelected(payment)
- Log.d("PaymentAdapter", "Payment selected: ${payment.name}")
+ Log.d("PaymentAdapter", "Payment selected: ${payment.bankName}")
}
}
@@ -118,7 +118,7 @@ class PaymentMethodAdapter(
}
// Set selected payment object
- fun setSelectedPayment(payment: PaymentInfoItem) {
+ fun setSelectedPayment(payment: PaymentItemDetail) {
setSelectedPaymentId(payment.id)
}
}
\ 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
index 694f1f7..4f02163 100644
--- 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
@@ -7,6 +7,8 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
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.response.customer.order.OrderListItemsItem
+import com.alya.ecommerce_serang.data.api.response.customer.order.Orders
import com.alya.ecommerce_serang.data.api.response.order.CompletedOrderResponse
import com.alya.ecommerce_serang.data.repository.OrderRepository
import com.alya.ecommerce_serang.data.repository.Result
@@ -26,6 +28,13 @@ class HistoryViewModel(private val repository: OrderRepository) : ViewModel() {
private val _orderCompletionStatus = MutableLiveData>()
val orderCompletionStatus: LiveData> = _orderCompletionStatus
+ private val _orderDetails = MutableLiveData()
+ val orderDetails: LiveData get() = _orderDetails
+
+ // LiveData untuk OrderItems
+ private val _orderItems = MutableLiveData>()
+ val orderItems: LiveData> get() = _orderItems
+
private val _isLoading = MutableLiveData()
val isLoading: LiveData = _isLoading
@@ -35,6 +44,9 @@ class HistoryViewModel(private val repository: OrderRepository) : ViewModel() {
private val _isSuccess = MutableLiveData()
val isSuccess: LiveData = _isSuccess
+ private val _error = MutableLiveData()
+ val error: LiveData get() = _error
+
fun getOrderList(status: String) {
_orders.value = ViewState.Loading
viewModelScope.launch {
@@ -99,4 +111,24 @@ class HistoryViewModel(private val repository: OrderRepository) : ViewModel() {
}
}
}
+
+ fun getOrderDetails(orderId: Int) {
+ _isLoading.value = true
+ viewModelScope.launch {
+ try {
+ val response = repository.getOrderDetails(orderId)
+ if (response != null) {
+ _orderDetails.value = response.orders
+ _orderItems.value = response.orders.orderItems
+ } else {
+ _error.value = "Gagal memuat detail pesanan"
+ }
+ } catch (e: Exception) {
+ _error.value = "Terjadi kesalahan: ${e.message}"
+ Log.e(TAG, "Error fetching order details", e)
+ } finally {
+ _isLoading.value = false
+ }
+ }
+ }
}
\ 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
index c6c0b99..45c3297 100644
--- 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
@@ -22,9 +22,13 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.alya.ecommerce_serang.R
import com.alya.ecommerce_serang.data.api.dto.OrdersItem
+import com.alya.ecommerce_serang.data.api.dto.ReviewUIItem
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.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
@@ -236,7 +240,7 @@ class OrderHistoryAdapter(
}
deadlineDate.apply {
visibility = View.VISIBLE
- text = formatShipmentDate(order.updatedAt, order.etd.toInt())
+ text = formatShipmentDate(order.updatedAt, order.etd ?: "0")
}
}
"delivered" -> {
@@ -262,6 +266,7 @@ class OrderHistoryAdapter(
visibility = View.VISIBLE
text = itemView.context.getString(R.string.add_review)
setOnClickListener {
+ addReviewProduct(order)
// Handle click event
}
}
@@ -322,9 +327,11 @@ class OrderHistoryAdapter(
}
}
- private fun formatShipmentDate(dateString: String, estimate: Int): String {
+ 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())
inputFormat.timeZone = TimeZone.getTimeZone("UTC")
@@ -339,7 +346,7 @@ class OrderHistoryAdapter(
calendar.time = it
// Add estimated days
- calendar.add(Calendar.DAY_OF_MONTH, estimate)
+ calendar.add(Calendar.DAY_OF_MONTH, estimateTD)
outputFormat.format(calendar.time)
} ?: dateString
} catch (e: Exception) {
@@ -485,10 +492,70 @@ class OrderHistoryAdapter(
}
dialog.show()
}
+
+ private fun addReviewProduct(order: OrdersItem) {
+ // Use ViewModel to fetch order details
+ viewModel.getOrderDetails(order.orderId)
+
+ // Create loading dialog
+// val loadingDialog = Dialog(itemView.context).apply {
+// requestWindowFeature(Window.FEATURE_NO_TITLE)
+// setContentView(R.layout.dialog_loading)
+// window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+// setCancelable(false)
+// }
+// loadingDialog.show()
+
+ viewModel.error.observe(itemView.findViewTreeLifecycleOwner()!!) { errorMsg ->
+ if (!errorMsg.isNullOrEmpty()) {
+ Toast.makeText(itemView.context, errorMsg, Toast.LENGTH_SHORT).show()
+ }
+ }
+
+ // Observe the order details result
+ viewModel.orderItems.observe(itemView.findViewTreeLifecycleOwner()!!) { orderItems ->
+ if (orderItems != null && orderItems.isNotEmpty()) {
+ // For single item review
+ if (orderItems.size == 1) {
+ val item = orderItems[0]
+ val intent = Intent(itemView.context, CreateReviewActivity::class.java).apply {
+ putExtra("order_item_id", item.orderItemId)
+ putExtra("product_name", item.productName)
+ putExtra("product_image", item.productImage)
+ }
+ (itemView.context as Activity).startActivityForResult(intent, REQUEST_CODE_REVIEW)
+ }
+ // For multiple items
+ else {
+ val reviewItems = orderItems.map { item ->
+ ReviewUIItem(
+ orderItemId = item.orderItemId,
+ productName = item.productName,
+ productImage = item.productImage
+ )
+ }
+
+ val itemsJson = Gson().toJson(reviewItems)
+ val intent = Intent(itemView.context, ReviewProductActivity::class.java).apply {
+ putExtra("order_items", itemsJson)
+ }
+ (itemView.context as Activity).startActivityForResult(intent, REQUEST_CODE_REVIEW)
+ }
+ } else {
+ Toast.makeText(
+ itemView.context,
+ "No items to review",
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ }
+ }
+
}
companion object {
private const val REQUEST_IMAGE_PICK = 100
+ const val REQUEST_CODE_REVIEW = 101
private var imagePickCallback: ((Uri) -> Unit)? = null
// This method should be called from the activity's onActivityResult
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/order/review/AddReviewAdapter.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/order/review/AddReviewAdapter.kt
new file mode 100644
index 0000000..248d47a
--- /dev/null
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/order/review/AddReviewAdapter.kt
@@ -0,0 +1,64 @@
+package com.alya.ecommerce_serang.ui.order.review
+
+import android.text.Editable
+import android.text.TextWatcher
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.alya.ecommerce_serang.R
+import com.alya.ecommerce_serang.data.api.dto.ReviewUIItem
+import com.alya.ecommerce_serang.databinding.ItemReviewProductBinding
+import com.bumptech.glide.Glide
+
+class AddReviewAdapter(
+ private val items: List,
+ private val onRatingChanged: (position: Int, rating: Int) -> Unit,
+ private val onReviewTextChanged: (position: Int, text: String) -> Unit
+) : RecyclerView.Adapter() {
+
+ inner class AddReviewViewHolder(private val binding: ItemReviewProductBinding) :
+ RecyclerView.ViewHolder(binding.root) {
+ fun bind(item: ReviewUIItem) {
+ binding.apply {
+ tvProductName.text = item.productName
+
+ Glide.with(itemView.context)
+ .load(item.productImage)
+ .placeholder(R.drawable.placeholder_image)
+ .error(R.drawable.placeholder_image)
+ .into(ivProduct)
+
+ ratingBar.rating = item.rating.toFloat()
+ etReviewText.setText(item.reviewText)
+
+ ratingBar.setOnRatingBarChangeListener { _, rating, _ ->
+ onRatingChanged(adapterPosition, rating.toInt())
+ }
+
+ etReviewText.addTextChangedListener(object : TextWatcher {
+ override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
+ override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
+ override fun afterTextChanged(editable: Editable?) {
+ onReviewTextChanged(adapterPosition, editable.toString())
+ }
+ })
+
+ }
+ }
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AddReviewViewHolder {
+ val binding = ItemReviewProductBinding.inflate(
+ LayoutInflater.from(parent.context),
+ parent,
+ false
+ )
+ return AddReviewViewHolder(binding)
+ }
+
+ override fun onBindViewHolder(holder: AddReviewViewHolder, position: Int) {
+ holder.bind(items[position])
+ }
+
+ override fun getItemCount(): Int = items.size
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/order/review/CreateReviewActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/order/review/CreateReviewActivity.kt
new file mode 100644
index 0000000..1fbbc93
--- /dev/null
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/order/review/CreateReviewActivity.kt
@@ -0,0 +1,188 @@
+package com.alya.ecommerce_serang.ui.order.review
+
+import android.os.Bundle
+import android.util.Log
+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.data.api.dto.ReviewProductItem
+import com.alya.ecommerce_serang.data.api.dto.ReviewUIItem
+import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
+import com.alya.ecommerce_serang.data.repository.OrderRepository
+import com.alya.ecommerce_serang.data.repository.Result
+import com.alya.ecommerce_serang.databinding.ActivityCreateReviewBinding
+import com.alya.ecommerce_serang.utils.BaseViewModelFactory
+import com.alya.ecommerce_serang.utils.SessionManager
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+
+class CreateReviewActivity : AppCompatActivity() {
+
+ private lateinit var binding: ActivityCreateReviewBinding
+ private lateinit var sessionManager: SessionManager
+ private val reviewItems = mutableListOf()
+ private var addReviewAdapter: AddReviewAdapter? = null
+
+ private val viewModel : CreateReviewViewModel by viewModels {
+ BaseViewModelFactory {
+ val apiService = ApiConfig.getApiService(sessionManager)
+ val orderRepository = OrderRepository(apiService)
+ CreateReviewViewModel(orderRepository)
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = ActivityCreateReviewBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ sessionManager = SessionManager(this)
+
+ WindowCompat.setDecorFitsSystemWindows(window, false)
+
+ enableEdgeToEdge()
+
+ ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view, windowInsets ->
+ val systemBars = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
+ view.setPadding(
+ systemBars.left,
+ systemBars.top,
+ systemBars.right,
+ systemBars.bottom
+ )
+ windowInsets
+ }
+
+ setupToolbar()
+ getIntentData()
+ setupRecyclerView()
+ observeViewModel()
+ setupSubmitButton()
+ }
+
+ private fun setupToolbar() {
+ setSupportActionBar(binding.toolbar)
+ supportActionBar?.setDisplayShowTitleEnabled(false)
+ binding.btnBack.setOnClickListener { onBackPressed() }
+ }
+
+ private fun getIntentData() {
+ // First check if multiple items were passed
+ val orderItemsJson = intent.getStringExtra("order_items")
+ if (orderItemsJson != null) {
+ try {
+ val type = object : TypeToken>() {}.type
+ val items: List = Gson().fromJson(orderItemsJson, type)
+
+ // Make sure we explicitly set rating and reviewText
+ reviewItems.addAll(items.map { item ->
+ ReviewUIItem(
+ orderItemId = item.orderItemId,
+ productName = item.productName,
+ productImage = item.productImage,
+ rating = 5, // Default to 5 stars
+ reviewText = "" // Empty by default
+ )
+ })
+ } catch (e: Exception) {
+ Toast.makeText(this, "Error loading review items", Toast.LENGTH_SHORT).show()
+ finish()
+ }
+ } else {
+ // Check if a single item was passed
+ val orderItemId = intent.getIntExtra("order_item_id", -1)
+ val productName = intent.getStringExtra("product_name") ?: ""
+ val productImage = intent.getStringExtra("product_image") ?: ""
+
+ if (orderItemId != -1) {
+ reviewItems.add(
+ ReviewUIItem(
+ orderItemId = orderItemId,
+ productName = productName,
+ productImage = productImage,
+ rating = 5, // Default to 5 stars
+ reviewText = "" // Empty by default
+ )
+ )
+ } else {
+ Toast.makeText(this, "No items to review", Toast.LENGTH_SHORT).show()
+ finish()
+ }
+ }
+ }
+
+ private fun setupRecyclerView() {
+ addReviewAdapter = AddReviewAdapter(
+ reviewItems,
+ onRatingChanged = { position, rating ->
+ reviewItems[position].rating = rating
+ },
+ onReviewTextChanged = { position, text ->
+ reviewItems[position].reviewText = text
+ }
+ )
+
+ binding.rvReviewItems.apply {
+ layoutManager = LinearLayoutManager(this@CreateReviewActivity)
+ adapter = addReviewAdapter
+ }
+ }
+
+ private fun observeViewModel() {
+ viewModel.reviewSubmitStatus.observe(this) { result ->
+ when (result) {
+ is Result.Loading -> {
+ // Show loading indicator
+ // You can add a ProgressBar in your layout and show/hide it here
+ }
+ is Result.Success -> {
+ // All reviews submitted successfully
+ Toast.makeText(this, "Ulasan berhasil dikirim", Toast.LENGTH_SHORT).show()
+ setResult(RESULT_OK)
+ finish()
+ }
+ is Result.Error -> {
+ // Show error message
+ Log.e("CreateReviewActivity", "Error: ${result.exception}")
+// Toast.makeText(this, result.message, Toast.LENGTH_SHORT).show()
+ }
+ }
+ }
+ }
+
+ private fun setupSubmitButton() {
+ binding.btnSubmitReview.setOnClickListener {
+ // Validate all reviews
+ var isValid = true
+ for (item in reviewItems) {
+ if (item.reviewText.isBlank()) {
+ isValid = false
+ Toast.makeText(this, "Mohon isi semua ulasan", Toast.LENGTH_SHORT).show()
+ break
+ }
+ }
+
+ // In setupSubmitButton() method
+ if (isValid) {
+ viewModel.setTotalReviewsToSubmit(reviewItems.size)
+
+ // Submit all reviews
+ for (item in reviewItems) {
+ Log.d("ReviewActivity", "Submitting review for item ${item.orderItemId}: rating=${item.rating}, text=${item.reviewText}")
+
+ val reviewProductItem = ReviewProductItem(
+ orderItemId = item.orderItemId,
+ rating = item.rating,
+ reviewTxt = item.reviewText
+ )
+ viewModel.submitReview(reviewProductItem)
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/order/review/CreateReviewViewModel.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/order/review/CreateReviewViewModel.kt
new file mode 100644
index 0000000..6c05ab0
--- /dev/null
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/order/review/CreateReviewViewModel.kt
@@ -0,0 +1,41 @@
+package com.alya.ecommerce_serang.ui.order.review
+
+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.ReviewProductItem
+import com.alya.ecommerce_serang.data.api.response.customer.order.CreateReviewResponse
+import com.alya.ecommerce_serang.data.repository.OrderRepository
+import com.alya.ecommerce_serang.data.repository.Result
+import kotlinx.coroutines.launch
+
+class CreateReviewViewModel(private val repository: OrderRepository): ViewModel() {
+ private val _reviewSubmitStatus = MutableLiveData>()
+ val reviewSubmitStatus: LiveData> = _reviewSubmitStatus
+
+ private val _reviewsSubmitted = MutableLiveData(0)
+ private var totalReviewsToSubmit = 0
+ private var anyFailures = false
+
+ fun submitReview(reviewItem: ReviewProductItem) {
+ viewModelScope.launch {
+ try {
+ _reviewSubmitStatus.value = Result.Loading
+ val result = repository.createReviewProduct(reviewItem)
+ _reviewSubmitStatus.value = result
+ } catch (e: Exception) {
+ anyFailures = true
+ Log.e("CreateReviewViewModel", "Error create review: ${e.message}")
+ _reviewSubmitStatus.value = Result.Error(e)
+ }
+ }
+ }
+
+ fun setTotalReviewsToSubmit(count: Int) {
+ totalReviewsToSubmit = count
+ _reviewsSubmitted.value = 0
+ anyFailures = false
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_create_review.xml b/app/src/main/res/layout/activity_create_review.xml
new file mode 100644
index 0000000..b516a95
--- /dev/null
+++ b/app/src/main/res/layout/activity_create_review.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_review_product.xml b/app/src/main/res/layout/item_review_product.xml
new file mode 100644
index 0000000..c6767a9
--- /dev/null
+++ b/app/src/main/res/layout/item_review_product.xml
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 25b40eb..c41219f 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -41,6 +41,7 @@
#E8ECF2
#7D8FAB
#489EC6
+ #faf069
#489EC6
#8E8E8E