From b3ab3de9640c60f78a498379a983f699568e8cee Mon Sep 17 00:00:00 2001 From: Gracia Hotmauli <95269134+hotmauligracia@users.noreply.github.com> Date: Mon, 12 May 2025 12:04:29 +0700 Subject: [PATCH 1/4] update --- .idea/deploymentTargetSelector.xml | 8 + app/src/main/AndroidManifest.xml | 23 +- .../store/orders/KonfirmasiTagihanResponse.kt | 1 + .../store/orders/OrderListResponse.kt | 183 ++++++----- .../store/orders/UpdateOrderItemResponse.kt | 79 ++--- .../data/api/retrofit/ApiService.kt | 19 +- .../data/repository/OrderRepository.kt | 31 -- .../data/repository/SellsRepository.kt | 45 +++ .../ui/profile/mystore/MyStoreActivity.kt | 33 +- .../ui/profile/mystore/sells/SellsActivity.kt | 36 ++- .../ui/profile/mystore/sells/SellsAdapter.kt | 288 ++++++++++-------- .../ui/profile/mystore/sells/SellsFragment.kt | 44 ++- .../mystore/sells/SellsListFragment.kt | 131 ++++++++ .../mystore/sells/SellsPagerAdapter.kt | 34 --- .../mystore/sells/SellsViewPagerAdapter.kt | 27 ++ .../sells/all_sells/AllSellsFragment.kt | 41 --- .../cancellation/CancellationFragment.kt | 41 --- .../sells/completed/CompletedFragment.kt | 41 --- .../failed_payment/FailedPaymentFragment.kt | 42 --- .../failed_shipment/FailedShipmentFragment.kt | 41 --- .../sells/order/DetailOrderActivity.kt | 21 ++ .../mystore/sells/order/OrderAdapter.kt | 60 ---- .../mystore/sells/order/OrderFragment.kt | 40 --- ...ntActivity.kt => DetailPaymentActivity.kt} | 4 +- .../mystore/sells/payment/PaymentAdapter.kt | 61 ---- .../mystore/sells/payment/PaymentFragment.kt | 40 --- ...nActivity.kt => DetailShipmentActivity.kt} | 4 +- .../mystore/sells/shipment/ShipmentAdapter.kt | 56 ---- .../sells/shipment/ShipmentFragment.kt | 41 --- .../mystore/sells/shipped/ShippedFragment.kt | 41 --- .../utils/viewmodel/SellsViewModel.kt | 59 +++- .../main/res/layout/activity_detail_order.xml | 9 + .../res/layout/activity_detail_payment.xml | 9 + .../res/layout/activity_detail_shipment.xml | 9 + app/src/main/res/layout/activity_sells.xml | 53 +++- .../main/res/layout/fragment_all_sells.xml | 6 - .../main/res/layout/fragment_cancellation.xml | 7 - .../main/res/layout/fragment_completed.xml | 7 - .../res/layout/fragment_failed_payment.xml | 7 - .../res/layout/fragment_failed_shipment.xml | 7 - app/src/main/res/layout/fragment_order.xml | 7 - .../res/layout/fragment_order_history.xml | 8 +- app/src/main/res/layout/fragment_payment.xml | 8 - .../main/res/layout/fragment_sells_list.xml | 38 +++ app/src/main/res/layout/fragment_shipment.xml | 7 - app/src/main/res/layout/fragment_shipped.xml | 7 - app/src/main/res/layout/item_sells_order.xml | 54 ++-- .../main/res/layout/item_sells_payment.xml | 57 ++-- .../main/res/layout/item_sells_shipment.xml | 50 +-- app/src/main/res/values/strings.xml | 2 +- 50 files changed, 875 insertions(+), 1092 deletions(-) create mode 100644 app/src/main/java/com/alya/ecommerce_serang/data/repository/SellsRepository.kt create mode 100644 app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsListFragment.kt delete mode 100644 app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsPagerAdapter.kt create mode 100644 app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsViewPagerAdapter.kt delete mode 100644 app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/all_sells/AllSellsFragment.kt delete mode 100644 app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/cancellation/CancellationFragment.kt delete mode 100644 app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/completed/CompletedFragment.kt delete mode 100644 app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/failed_payment/FailedPaymentFragment.kt delete mode 100644 app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/failed_shipment/FailedShipmentFragment.kt create mode 100644 app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/order/DetailOrderActivity.kt delete mode 100644 app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/order/OrderAdapter.kt delete mode 100644 app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/order/OrderFragment.kt rename app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/payment/{ClaimPaymentActivity.kt => DetailPaymentActivity.kt} (86%) delete mode 100644 app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/payment/PaymentAdapter.kt delete mode 100644 app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/payment/PaymentFragment.kt rename app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipment/{ShippingConfirmationActivity.kt => DetailShipmentActivity.kt} (85%) delete mode 100644 app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipment/ShipmentAdapter.kt delete mode 100644 app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipment/ShipmentFragment.kt delete mode 100644 app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipped/ShippedFragment.kt create mode 100644 app/src/main/res/layout/activity_detail_order.xml create mode 100644 app/src/main/res/layout/activity_detail_payment.xml create mode 100644 app/src/main/res/layout/activity_detail_shipment.xml delete mode 100644 app/src/main/res/layout/fragment_all_sells.xml delete mode 100644 app/src/main/res/layout/fragment_cancellation.xml delete mode 100644 app/src/main/res/layout/fragment_completed.xml delete mode 100644 app/src/main/res/layout/fragment_failed_payment.xml delete mode 100644 app/src/main/res/layout/fragment_failed_shipment.xml delete mode 100644 app/src/main/res/layout/fragment_order.xml delete mode 100644 app/src/main/res/layout/fragment_payment.xml create mode 100644 app/src/main/res/layout/fragment_sells_list.xml delete mode 100644 app/src/main/res/layout/fragment_shipment.xml delete mode 100644 app/src/main/res/layout/fragment_shipped.xml diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml index b268ef3..199a303 100644 --- a/.idea/deploymentTargetSelector.xml +++ b/.idea/deploymentTargetSelector.xml @@ -4,6 +4,14 @@ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fada1ca..a5bd224 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,13 +7,15 @@ - - + + + + + @@ -71,12 +82,6 @@ - - diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/orders/KonfirmasiTagihanResponse.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/orders/KonfirmasiTagihanResponse.kt index af331d4..2e3df6a 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/orders/KonfirmasiTagihanResponse.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/orders/KonfirmasiTagihanResponse.kt @@ -1,5 +1,6 @@ package com.alya.ecommerce_serang.data.api.response.store.orders +import com.alya.ecommerce_serang.data.api.dto.UpdatedOrder import com.google.gson.annotations.SerializedName data class KonfirmasiTagihanResponse( diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/orders/OrderListResponse.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/orders/OrderListResponse.kt index 64725ae..e9b00c3 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/orders/OrderListResponse.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/orders/OrderListResponse.kt @@ -1,6 +1,5 @@ package com.alya.ecommerce_serang.data.api.response.store.orders -import com.alya.ecommerce_serang.data.api.dto.OrdersItem import com.google.gson.annotations.SerializedName data class OrderListResponse( @@ -12,43 +11,133 @@ data class OrderListResponse( val message: String? = null ) -data class Voucher( - - @field:SerializedName("name") - val name: Any? = null, - - @field:SerializedName("voucher_id") - val voucherId: Any? = null, - - @field:SerializedName("voucher_code") - val voucherCode: Any? = null -) - -data class Shipment( +data class OrdersItem( @field:SerializedName("receipt_num") val receiptNum: Any? = null, - @field:SerializedName("courier") - val courier: Any? = null, + @field:SerializedName("payment_upload_at") + val paymentUploadAt: Any? = null, - @field:SerializedName("price") - val price: Any? = null, + @field:SerializedName("latitude") + val latitude: String? = null, + + @field:SerializedName("pay_info_name") + val payInfoName: String? = null, + + @field:SerializedName("created_at") + val createdAt: String? = null, + + @field:SerializedName("voucher_code") + val voucherCode: Any? = null, + + @field:SerializedName("updated_at") + val updatedAt: String? = null, + + @field:SerializedName("etd") + val etd: String? = null, + + @field:SerializedName("street") + val street: String? = null, + + @field:SerializedName("cancel_date") + val cancelDate: String? = null, + + @field:SerializedName("payment_evidence") + val paymentEvidence: Any? = null, + + @field:SerializedName("longitude") + val longitude: String? = null, + + @field:SerializedName("shipment_status") + val shipmentStatus: String? = null, + + @field:SerializedName("order_items") + val orderItems: List? = null, + + @field:SerializedName("auto_completed_at") + val autoCompletedAt: Any? = null, + + @field:SerializedName("is_store_location") + val isStoreLocation: Boolean? = null, + + @field:SerializedName("qris_image") + val qrisImage: String? = null, + + @field:SerializedName("voucher_name") + val voucherName: Any? = null, + + @field:SerializedName("payment_status") + val paymentStatus: Any? = null, + + @field:SerializedName("address_id") + val addressId: Int? = null, + + @field:SerializedName("is_negotiable") + val isNegotiable: Boolean? = null, + + @field:SerializedName("payment_amount") + val paymentAmount: Any? = null, + + @field:SerializedName("cancel_reason") + val cancelReason: String? = null, + + @field:SerializedName("user_id") + val userId: Int? = null, + + @field:SerializedName("total_amount") + val totalAmount: String? = null, + + @field:SerializedName("province_id") + val provinceId: Int? = null, + + @field:SerializedName("courier") + val courier: String? = null, + + @field:SerializedName("subdistrict") + val subdistrict: String? = null, @field:SerializedName("service") - val service: Any? = null, + val service: String? = null, - @field:SerializedName("shipment_id") - val shipmentId: Any? = null, + @field:SerializedName("pay_info_num") + val payInfoNum: String? = null, + + @field:SerializedName("shipment_price") + val shipmentPrice: String? = null, + + @field:SerializedName("voucher_id") + val voucherId: Any? = null, + + @field:SerializedName("payment_info_id") + val paymentInfoId: Int? = null, + + @field:SerializedName("detail") + val detail: String? = null, + + @field:SerializedName("postal_code") + val postalCode: String? = null, + + @field:SerializedName("order_id") + val orderId: Int? = null, + + @field:SerializedName("username") + val username: String? = null, @field:SerializedName("status") - val status: Any? = null + val status: String? = null, + + @field:SerializedName("city_id") + val cityId: Int? = null ) data class OrderItemsItem( + @field:SerializedName("order_item_id") + val orderItemId: Int? = null, + @field:SerializedName("review_id") - val reviewId: Int? = null, + val reviewId: Any? = null, @field:SerializedName("quantity") val quantity: Int? = null, @@ -62,6 +151,9 @@ data class OrderItemsItem( @field:SerializedName("product_image") val productImage: String? = null, + @field:SerializedName("product_id") + val productId: Int? = null, + @field:SerializedName("store_name") val storeName: String? = null, @@ -71,48 +163,3 @@ data class OrderItemsItem( @field:SerializedName("product_name") val productName: String? = null ) - -data class Address( - - @field:SerializedName("is_store_location") - val isStoreLocation: Boolean? = null, - - @field:SerializedName("province_id") - val provinceId: Int? = null, - - @field:SerializedName("street") - val street: String? = null, - - @field:SerializedName("subdistrict") - val subdistrict: String? = null, - - @field:SerializedName("latitude") - val latitude: Any? = null, - - @field:SerializedName("address_id") - val addressId: Int? = null, - - @field:SerializedName("detail") - val detail: String? = null, - - @field:SerializedName("postal_code") - val postalCode: String? = null, - - @field:SerializedName("longitude") - val longitude: Any? = null, - - @field:SerializedName("city_id") - val cityId: Int? = null -) - -data class Payment( - - @field:SerializedName("evidence") - val evidence: Any? = null, - - @field:SerializedName("uploaded_at") - val uploadedAt: Any? = null, - - @field:SerializedName("payment_id") - val paymentId: Any? = null -) diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/orders/UpdateOrderItemResponse.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/orders/UpdateOrderItemResponse.kt index 9e6b08c..f56b81b 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/orders/UpdateOrderItemResponse.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/orders/UpdateOrderItemResponse.kt @@ -4,72 +4,27 @@ import com.google.gson.annotations.SerializedName data class UpdateOrderItemResponse( - @field:SerializedName("message") - val message: String? = null, - - @field:SerializedName("updatedOrder") - val updatedOrder: UpdatedOrder? = null, - - @field:SerializedName("updatedItems") - val updatedItems: List? = null -) - -data class UpdatedItemsItem( - - @field:SerializedName("quantity") - val quantity: Int? = null, - - @field:SerializedName("price") - val price: String? = null, - - @field:SerializedName("subtotal") - val subtotal: String? = null, - - @field:SerializedName("product_id") - val productId: Int? = null, - - @field:SerializedName("id") - val id: Int? = null, + @field:SerializedName("total_amount") + val totalAmount: Int? = null, @field:SerializedName("order_id") - val orderId: Int? = null -) + val orderId: Int? = null, -data class UpdatedOrder( - - @field:SerializedName("auto_canceled_at") - val autoCanceledAt: String? = null, - - @field:SerializedName("payment_method_id") - val paymentMethodId: Int? = null, - - @field:SerializedName("auto_completed_at") - val autoCompletedAt: String? = null, - - @field:SerializedName("updated_at") - val updatedAt: String? = null, - - @field:SerializedName("total_amount") - val totalAmount: String? = null, - - @field:SerializedName("user_id") - val userId: Int? = null, - - @field:SerializedName("address_id") - val addressId: Int? = null, - - @field:SerializedName("is_negotiable") - val isNegotiable: Boolean? = null, - - @field:SerializedName("created_at") - val createdAt: String? = null, - - @field:SerializedName("voucher_id") - val voucherId: Any? = null, - - @field:SerializedName("id") - val id: Int? = null, + @field:SerializedName("items") + val items: List? = null, @field:SerializedName("status") val status: String? = null ) + +data class ItemsItem( + + @field:SerializedName("order_item_id") + val orderItemId: Int? = null, + + @field:SerializedName("price") + val price: Int? = null, + + @field:SerializedName("subtotal") + val subtotal: Int? = null +) 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 15d6858..f883032 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 @@ -45,8 +45,6 @@ import retrofit2.Call import retrofit2.Response import retrofit2.http.Body import retrofit2.http.DELETE -import retrofit2.http.Field -import retrofit2.http.FormUrlEncoded import retrofit2.http.GET import retrofit2.http.Multipart import retrofit2.http.POST @@ -204,19 +202,22 @@ interface ApiService { suspend fun getListProv( ): Response - @GET("mystore/orders") - suspend fun getAllOrders(): Response - - @GET("mystore/orders/{status}") - suspend fun getOrdersByStatus( - @Query("status") status: String - ): Response + @GET("order/{status}") + suspend fun getSellList( + @Path("status") status: String + ): Response @PUT("store/order/update") suspend fun confirmOrder( @Body confirmOrder : CompletedOrderRequest ): Response + @PUT("store/order/update") + suspend fun updateOrder( + @Query("order_id") orderId: Int?, + @Query("status") status: String + ): Response + @Multipart @POST("addcomplaint") suspend fun addComplaint( 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 3378880..e0bdc01 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 @@ -7,7 +7,6 @@ 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.OrdersItem import com.alya.ecommerce_serang.data.api.dto.UserProfile import com.alya.ecommerce_serang.data.api.response.customer.cart.DataItem import com.alya.ecommerce_serang.data.api.response.customer.order.CreateOrderResponse @@ -234,35 +233,6 @@ class OrderRepository(private val apiService: ApiService) { return if (response.isSuccessful) response.body() else null } - suspend fun fetchSells(): List { - return try { - val response = apiService.getAllOrders() // Replace with the actual method from your ApiService - if (response.isSuccessful) { - response.body()?.orders ?: emptyList() // Assuming the response body has 'orders' - } else { - Log.e("OrderRepository", "Error fetching all sells. Code: ${response.code()}") - emptyList() - } - } catch (e: Exception) { - Log.e("OrderRepository", "Exception fetching sells", e) - emptyList() - } - } - - suspend fun fetchOrdersByStatus(status: String): List { - return try { - val response = apiService.getOrdersByStatus(status) // Replace with actual method for status-based fetch - if (response.isSuccessful) { - response.body()?.orders?.filterNotNull() ?: emptyList() // Assuming the response body has 'orders' - } else { - Log.e("OrderRepository", "Error fetching orders by status ($status). Code: ${response.code()}") - emptyList() - } - } catch (e: Exception) { - Log.e("OrderRepository", "Exception fetching orders by status", e) - emptyList() - } - } suspend fun fetchUserProfile(): Result { return try { val response = apiService.getUserProfile() @@ -461,5 +431,4 @@ suspend fun uploadPaymentProof(request: AddEvidenceMultipartRequest): Result { + return try { + Log.d("SellsRepository", "Add Evidence : $status") + val response = apiService.getSellList(status) + + if (response.isSuccessful) { + val allListSell = response.body() + if (allListSell != null) { + Log.d("SellsRepository", "Add Evidence successfully: ${allListSell.message}") + Result.Success(allListSell) + } else { + Log.e("SellsRepository", "Response body was null") + Result.Error(Exception("Empty response from server")) + } + } else { + val errorBody = response.errorBody()?.string() ?: "Unknown error" + Log.e("SellsRepository", "Error Add Evidence : $errorBody") + Result.Error(Exception(errorBody)) + } + } catch (e: Exception) { + Log.e("SellsRepository", "Exception Add Evidence ", e) + Result.Error(e) + } + } + + suspend fun updateOrderStatus(orderId: Int?, status: String) { + try { + val response = apiService.updateOrder(orderId, status) + if (response.isSuccessful) { + Log.d("SellsRepository", "Order status updated successfully: orderId=$orderId, status=$status") + } else { + Log.e("SellsRepository", "Error updating order status: orderId=$orderId, status=$status") + } + } catch (e: Exception) { + Log.e("SellsRepository", "Exception updating order status", e) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/MyStoreActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/MyStoreActivity.kt index 08efbbe..91be929 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/MyStoreActivity.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/MyStoreActivity.kt @@ -16,10 +16,7 @@ import com.alya.ecommerce_serang.ui.profile.mystore.balance.BalanceActivity import com.alya.ecommerce_serang.ui.profile.mystore.product.ProductActivity import com.alya.ecommerce_serang.ui.profile.mystore.profile.DetailStoreProfileActivity import com.alya.ecommerce_serang.ui.profile.mystore.review.ReviewFragment -import com.alya.ecommerce_serang.ui.profile.mystore.sells.all_sells.AllSellsFragment -import com.alya.ecommerce_serang.ui.profile.mystore.sells.order.OrderFragment -import com.alya.ecommerce_serang.ui.profile.mystore.sells.payment.PaymentFragment -import com.alya.ecommerce_serang.ui.profile.mystore.sells.shipment.ShipmentFragment +import com.alya.ecommerce_serang.ui.profile.mystore.sells.SellsListFragment import com.alya.ecommerce_serang.utils.BaseViewModelFactory import com.alya.ecommerce_serang.utils.SessionManager import com.alya.ecommerce_serang.utils.viewmodel.MyStoreViewModel @@ -84,31 +81,19 @@ class MyStoreActivity : AppCompatActivity() { } binding.tvHistory.setOnClickListener { - supportFragmentManager.beginTransaction() - .replace(android.R.id.content, AllSellsFragment()) - .addToBackStack(null) - .commit() + navigateToSellsFragment("all") } binding.layoutPerluTagihan.setOnClickListener { - supportFragmentManager.beginTransaction() - .replace(android.R.id.content, OrderFragment()) - .addToBackStack(null) - .commit() + navigateToSellsFragment("pending") } binding.layoutPembayaran.setOnClickListener { - supportFragmentManager.beginTransaction() - .replace(android.R.id.content, PaymentFragment()) - .addToBackStack(null) - .commit() + navigateToSellsFragment("paid") } binding.layoutPerluDikirim.setOnClickListener { - supportFragmentManager.beginTransaction() - .replace(android.R.id.content, ShipmentFragment()) - .addToBackStack(null) - .commit() + navigateToSellsFragment("processed") } binding.layoutProductMenu.setOnClickListener { @@ -129,4 +114,12 @@ class MyStoreActivity : AppCompatActivity() { .commit() } } + + private fun navigateToSellsFragment(status: String) { + val sellsFragment = SellsListFragment.newInstance(status) + supportFragmentManager.beginTransaction() + .replace(android.R.id.content, sellsFragment) + .addToBackStack(null) + .commit() + } } \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsActivity.kt index ebe50a4..a3e0c85 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsActivity.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsActivity.kt @@ -2,18 +2,48 @@ package com.alya.ecommerce_serang.ui.profile.mystore.sells import androidx.appcompat.app.AppCompatActivity import android.os.Bundle +import androidx.activity.viewModels import com.alya.ecommerce_serang.R -import com.alya.ecommerce_serang.ui.profile.mystore.sells.all_sells.AllSellsFragment +import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig +import com.alya.ecommerce_serang.data.repository.SellsRepository +import com.alya.ecommerce_serang.databinding.ActivitySellsBinding +import com.alya.ecommerce_serang.utils.BaseViewModelFactory +import com.alya.ecommerce_serang.utils.SessionManager +import com.alya.ecommerce_serang.utils.viewmodel.SellsViewModel class SellsActivity : AppCompatActivity() { + private lateinit var binding: ActivitySellsBinding + private lateinit var sessionManager: SessionManager + + private val viewModel: SellsViewModel by viewModels { + BaseViewModelFactory { + val apiService = ApiConfig.getApiService(sessionManager) + val sellsRepository = SellsRepository(apiService) + SellsViewModel(sellsRepository) + } + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_sells) + binding = ActivitySellsBinding.inflate(layoutInflater) + setContentView(binding.root) + + sessionManager = SessionManager(this) + + setupHeader() + if (savedInstanceState == null) { supportFragmentManager.beginTransaction() - .replace(R.id.sells_fragment_container, AllSellsFragment()) + .replace(R.id.fragment_container_sells, SellsFragment()) .commit() } } + + private fun setupHeader() { + binding.header.headerTitle.text = "Penjualan Saya" + + binding.header.headerLeftIcon.setOnClickListener { + onBackPressedDispatcher.onBackPressed() + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsAdapter.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsAdapter.kt index d1d6f61..b4ccccd 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsAdapter.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsAdapter.kt @@ -1,152 +1,188 @@ package com.alya.ecommerce_serang.ui.profile.mystore.sells +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Button +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.dto.OrdersItem +import com.alya.ecommerce_serang.data.api.response.store.orders.OrdersItem +import com.alya.ecommerce_serang.utils.viewmodel.SellsViewModel +import com.bumptech.glide.Glide +import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Locale +import java.util.TimeZone -class SellsAdapter : RecyclerView.Adapter() { - private var orderList: List = emptyList() +class SellsAdapter( + private val onOrderClickListener: (OrdersItem) -> Unit, + private val viewModel: SellsViewModel +) : RecyclerView.Adapter() { - // View Types for different statuses - private val TYPE_PENDING = 0 - private val TYPE_PAYMENT = 1 - private val TYPE_SHIPMENT = 2 - private val TYPE_COMPLETED = 3 - private val TYPE_FAILED_PAYMENT = 4 - private val TYPE_FAILED_SHIPMENT = 5 + private val sells = mutableListOf() + private var fragmentStatus: String = "all" - // Method to submit list to the adapter - fun submitList(orders: List?) { - orderList = orders ?: emptyList() + fun setFragmentStatus(status: String) { + fragmentStatus = status + } + + fun submitList(newSells: List) { + sells.clear() + sells.addAll(newSells) notifyDataSetChanged() } - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { - return when (viewType) { - TYPE_PENDING -> { - val view = LayoutInflater.from(parent.context).inflate(R.layout.item_sells_order, parent, false) - OrderViewHolder(view) + fun findResource(status: String): Int { + return when (status) { + "pending" -> R.layout.item_sells_order + "paid" -> R.layout.item_sells_payment + "processed" -> R.layout.item_sells_shipment + else -> R.layout.item_sells_payment + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SellsViewHolder { + val view = LayoutInflater.from(parent.context).inflate(findResource(fragmentStatus), parent, false) + return SellsViewHolder(view) + } + + override fun onBindViewHolder(holder: SellsViewHolder, position: Int) { + holder.bind(sells[position]) + } + + override fun getItemCount(): Int = sells.size + + inner class SellsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + private val tvSellsTitle: TextView = itemView.findViewById(R.id.tv_sells_title) + private val tvSellsNumber: TextView = itemView.findViewById(R.id.tv_sells_number) + private val tvSellsDueDesc: TextView = itemView.findViewById(R.id.tv_sells_due_desc) + private val tvSellsDue: TextView = itemView.findViewById(R.id.tv_sells_due) + private val tvSellsLocation: TextView = itemView.findViewById(R.id.tv_sells_location) + private val tvSellsCustomer: TextView = itemView.findViewById(R.id.tv_sells_customer) + private val ivSellsProduct: ImageView = itemView.findViewById(R.id.iv_sells_product) + private val tvSellsProductName: TextView = itemView.findViewById(R.id.tv_sells_product_name) + private val tvSellsProductQty: TextView = itemView.findViewById(R.id.tv_sells_product_qty) + private val tvSellsProductPrice: TextView = itemView.findViewById(R.id.tv_sells_product_price) + private val tvSeeMore: TextView = itemView.findViewById(R.id.tv_see_more) + private val tvSellsQty: TextView = itemView.findViewById(R.id.tv_sells_qty) + private val tvSellsPrice: TextView = itemView.findViewById(R.id.tv_sells_price) + private val btnEditOrder: Button = itemView.findViewById(R.id.btn_edit_order) + private val btnConfirmOrder: Button = itemView.findViewById(R.id.btn_confirm_order) + private val btnConfirmPayment: Button = itemView.findViewById(R.id.btn_confirm_payment) + private val btnConfirmShipment: Button = itemView.findViewById(R.id.btn_confirm_shipment) + + fun bind(sells: OrdersItem) { + + tvSellsNumber.text = "No. Pesanan: ${sells.orderId}" + tvSellsLocation.text = sells.subdistrict + tvSellsCustomer.text = sells.username + + val product = sells.orderItems?.get(0) + product?.let { + tvSellsProductName.text = it.productName + tvSellsProductQty.text = "x${it.quantity}" + tvSellsProductPrice.text = "Rp${it.price}" + + Glide.with(itemView.context) + .load(it.productImage) + .placeholder(R.drawable.placeholder_image) + .into(ivSellsProduct) } - TYPE_PAYMENT -> { - val view = LayoutInflater.from(parent.context).inflate(R.layout.item_sells_payment, parent, false) - PaymentViewHolder(view) + + sells.orderItems?.size?.let { + if (it > 1) { + tvSeeMore.visibility = View.VISIBLE + tvSeeMore.text = "Lihat ${it.minus(1)} produk lainnya" + } else { + tvSeeMore.visibility = View.GONE + } } - TYPE_SHIPMENT -> { - val view = LayoutInflater.from(parent.context).inflate(R.layout.item_sells_shipment, parent, false) - ShipmentViewHolder(view) - } -// TYPE_COMPLETED -> { -// val view = LayoutInflater.from(parent.context).inflate(R.layout.item_sells_completed, parent, false) -// CompletedViewHolder(view) -// } -// TYPE_FAILED_PAYMENT -> { -// val view = LayoutInflater.from(parent.context).inflate(R.layout.item_sells_failed_payment, parent, false) -// FailedPaymentViewHolder(view) -// } -// TYPE_FAILED_SHIPMENT -> { -// val view = LayoutInflater.from(parent.context).inflate(R.layout.item_sells_failed_shipment, parent, false) -// FailedShipmentViewHolder(view) -// } - else -> { - val view = LayoutInflater.from(parent.context).inflate(R.layout.item_sells_order, parent, false) - OrderViewHolder(view) + + tvSellsQty.text = "${sells.orderItems?.size} produk" + tvSellsPrice.text = "Rp${sells.totalAmount}" + + adjustDisplay(fragmentStatus, sells) + } + + private fun adjustDisplay(status: String, sells: OrdersItem) { + Log.d("SellsAdapter", "Adjusting display for status: $status") + + when (status) { + "pending" -> { + tvSellsDue.text = formatDueDate(sells.updatedAt.toString(), 3) + btnEditOrder.setOnClickListener { + TODO("Go to DetailOrderActivity") + } + btnConfirmOrder.setOnClickListener { + viewModel.updateOrderStatus(sells.orderId, "unpaid") + } + } + "paid" -> { + tvSellsDue.text = formatDueDate(sells.updatedAt.toString(), 1) + btnConfirmPayment.setOnClickListener { + TODO("Go to DetailPaymentActivity") + } + } + "processed" -> { + tvSellsDue.text = formatDueDate(sells.updatedAt.toString(), 2) + btnConfirmShipment.setOnClickListener { + TODO("Go to DetailShipmentActivity") + } + } + "shipped" -> { + tvSellsTitle.text = "Pesanan Telah Dikirim" + tvSellsDueDesc.text = "Dikirimkan pada" + tvSellsDue.text = formatDueDate(sells.updatedAt.toString(), 0) + tvSellsDue.background = itemView.context.getDrawable(R.drawable.bg_product_inactive) + btnConfirmPayment.visibility = View.GONE + } + "delivered" -> { + tvSellsTitle.text = "Pesanan Telah Dikirim" + tvSellsDueDesc.text = "Dikirimkan pada" + tvSellsDue.text = formatDueDate(sells.updatedAt.toString(), 0) + tvSellsDue.background = itemView.context.getDrawable(R.drawable.bg_product_inactive) + btnConfirmPayment.visibility = View.GONE + } + "completed" -> { + tvSellsTitle.text = "Pesanan Selesai" + tvSellsDueDesc.text = "Selesai pada" + tvSellsDue.text = formatDueDate(sells.updatedAt.toString(), 0) + tvSellsDue.background = itemView.context.getDrawable(R.drawable.bg_product_inactive) + btnConfirmPayment.visibility = View.GONE + } + "canceled" -> { + tvSellsTitle.text = "Pesanan Dibatalkan" + tvSellsDueDesc.text = "Dibatalkan pada" + tvSellsDue.text = formatDueDate(sells.updatedAt.toString(), 0) + tvSellsDue.background = itemView.context.getDrawable(R.drawable.bg_product_inactive) + btnConfirmPayment.visibility = View.GONE + } } } - } - // Determine the view type based on the order status - override fun getItemViewType(position: Int): Int { - val order = orderList[position] - return when (order?.shipmentStatus) { - "pending" -> TYPE_PENDING - "paid" -> TYPE_PAYMENT - "shipped" -> TYPE_SHIPMENT - "completed" -> TYPE_COMPLETED - "failedPayment" -> TYPE_FAILED_PAYMENT - "failedShipment" -> TYPE_FAILED_SHIPMENT - else -> TYPE_PENDING // Default to pending if no status is matched - } - } + private fun formatDueDate(date: String, dueDay: Int): String { + return try { + val inputFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault()) + inputFormat.timeZone = TimeZone.getTimeZone("UTC") - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - val order = orderList[position] - when (holder) { - is OrderViewHolder -> holder.bind(order) - is PaymentViewHolder -> holder.bind(order) - is ShipmentViewHolder -> holder.bind(order) - is CompletedViewHolder -> holder.bind(order) - is FailedPaymentViewHolder -> holder.bind(order) - is FailedShipmentViewHolder -> holder.bind(order) - } - } + val outputFormat = SimpleDateFormat("dd MM; HH.mm", Locale("id", "ID")) - override fun getItemCount(): Int = orderList.size + val date = inputFormat.parse(date) - // ViewHolder for 'pending' status (Order) - class OrderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - private val tvOrderNumber: TextView = itemView.findViewById(R.id.tv_order_number) - private val tvOrderCustomer: TextView = itemView.findViewById(R.id.tv_order_customer) - private val tvOrderPrice: TextView = itemView.findViewById(R.id.tv_order_price) + date?.let { + val calendar = Calendar.getInstance() + calendar.time = it + calendar.add(Calendar.DATE, dueDay) - fun bind(order: OrdersItem?) { - tvOrderNumber.text = "Order #${order?.orderId}" - tvOrderCustomer.text = order?.userId.toString() - tvOrderPrice.text = "Total: ${order?.totalAmount}" - } - } - - // ViewHolder for 'paid' status (Payment) - class PaymentViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - private val tvPaymentNumber: TextView = itemView.findViewById(R.id.tv_payment_number) - private val tvPaymentCustomer: TextView = itemView.findViewById(R.id.tv_payment_customer) - private val tvPaymentPrice: TextView = itemView.findViewById(R.id.tv_payment_price) - - fun bind(order: OrdersItem?) { - tvPaymentNumber.text = "Order #${order?.orderId}" - tvPaymentCustomer.text = order?.userId.toString() - tvPaymentPrice.text = "Paid: ${order?.totalAmount}" - } - } - - // ViewHolder for 'shipped' status (Shipment) - class ShipmentViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - private val tvShipmentNumber: TextView = itemView.findViewById(R.id.tv_shipment_number) - private val tvShipmentLocation: TextView = itemView.findViewById(R.id.tv_shipment_location) - - fun bind(order: OrdersItem?) { - tvShipmentNumber.text = "Shipment #${order?.orderId}" - tvShipmentLocation.text = "Location: ${order?.addressId.toString()}" - } - } - - // ViewHolder for 'completed' status - class CompletedViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - //private val tvCompletedNumber: TextView = itemView.findViewById(R.id.tv_completed_number) - - fun bind(order: OrdersItem?) { - // tvCompletedNumber.text = "Completed Order #${order?.orderId}" - } - } - - // ViewHolder for 'failedPayment' status - class FailedPaymentViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - //private val tvFailedPaymentNumber: TextView = itemView.findViewById(R.id.tv_failed_payment_number) - - fun bind(order: OrdersItem?) { - //tvFailedPaymentNumber.text = "Failed Payment Order #${order?.orderId}" - } - } - - // ViewHolder for 'failedShipment' status - class FailedShipmentViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - //private val tvFailedShipmentNumber: TextView = itemView.findViewById(R.id.tv_failed_shipment_number) - - fun bind(order: OrdersItem?) { - //tvFailedShipmentNumber.text = "Failed Shipment Order #${order?.orderId}" + outputFormat.format(calendar.time) + } ?: date + } catch (e: Exception) { + Log.e("DueDateFormatting", "Error formatting date: ${e.message}") + date + }.toString() } } } \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsFragment.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsFragment.kt index 04a816a..cc38b01 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsFragment.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsFragment.kt @@ -1,45 +1,57 @@ package com.alya.ecommerce_serang.ui.profile.mystore.sells -import androidx.lifecycle.ViewModelProvider import android.os.Bundle import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.viewpager2.widget.ViewPager2 import com.google.android.material.tabs.TabLayoutMediator -import com.alya.ecommerce_serang.R -import com.alya.ecommerce_serang.utils.viewmodel.SellsViewModel +import com.alya.ecommerce_serang.databinding.FragmentSellsBinding +import com.alya.ecommerce_serang.utils.SessionManager class SellsFragment : Fragment() { - private lateinit var viewModel: SellsViewModel + + private var _binding: FragmentSellsBinding? = null + private val binding get() = _binding!! + private lateinit var sessionManager: SessionManager + + private lateinit var viewPagerAdapter: SellsViewPagerAdapter override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_sells, container, false) + _binding = FragmentSellsBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + sessionManager = SessionManager(requireContext()) -// val repository = OrderRepository(ApiService.create()) - viewModel = ViewModelProvider(this)[SellsViewModel::class.java] + viewPagerAdapter = SellsViewPagerAdapter(requireActivity()) + binding.viewPagerSells.adapter = viewPagerAdapter val tabs = listOf( - "Semua Pesanan", "Perlu Tagihan", "Konfirmasi Pembayaran", - "Perlu Dikirim", "Dikirim", "Selesai", - "Pembatalan", "Klaim Pembayaran", "Pengiriman Gagal" + "Semua Pesanan", + "Perlu Tagihan", + "Konfirmasi Pembayaran", + "Perlu Dikirim", + "Dikirim", + "Selesai", + "Pembatalan", + "Klaim Pembayaran", + "Pengiriman Gagal" ) - val adapter = SellsPagerAdapter(this, tabs.size) - val viewPager: ViewPager2 = view.findViewById(R.id.view_pager_sells) - viewPager.adapter = adapter - - TabLayoutMediator(view.findViewById(R.id.tab_layout_sells), viewPager) { tab, position -> + TabLayoutMediator(binding.tabLayoutSells, binding.viewPagerSells) { tab, position -> tab.text = tabs[position] }.attach() } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } } diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsListFragment.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsListFragment.kt new file mode 100644 index 0000000..0e25cc6 --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsListFragment.kt @@ -0,0 +1,131 @@ +package com.alya.ecommerce_serang.ui.profile.mystore.sells + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.viewModels +import androidx.recyclerview.widget.LinearLayoutManager +import com.alya.ecommerce_serang.data.api.response.store.orders.OrdersItem +import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig +import com.alya.ecommerce_serang.data.repository.SellsRepository +import com.alya.ecommerce_serang.databinding.FragmentSellsListBinding +import com.alya.ecommerce_serang.ui.order.address.ViewState +import com.alya.ecommerce_serang.utils.BaseViewModelFactory +import com.alya.ecommerce_serang.utils.SessionManager +import com.alya.ecommerce_serang.utils.viewmodel.SellsViewModel + +class SellsListFragment : Fragment() { + + private var _binding: FragmentSellsListBinding? = null + private val binding get() = _binding!! + private lateinit var sessionManager: SessionManager + + private val viewModel: SellsViewModel by viewModels { + BaseViewModelFactory { + val apiService = ApiConfig.getApiService(sessionManager) + val sellsRepository = SellsRepository(apiService) + SellsViewModel(sellsRepository) + } + } + + private lateinit var sellsAdapter: SellsAdapter + private var status: String = "all" + + 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 = FragmentSellsListBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + setupRecyclerView() + observeSellsList() + loadSells() + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + companion object { + private const val ARG_STATUS = "status" + + fun newInstance(status: String): SellsListFragment { + return SellsListFragment().apply { + arguments = Bundle().apply { + putString(ARG_STATUS, status) + } + } + } + } + + private fun setupRecyclerView() { + sellsAdapter = SellsAdapter( + onOrderClickListener = { sells -> + // Handle order click + navigateToSellsDetail(sells) + }, + viewModel = viewModel + ) + + sellsAdapter.setFragmentStatus(status) + + binding.rvSells.apply { + layoutManager = LinearLayoutManager(requireContext()) + adapter = sellsAdapter + } + } + + private fun observeSellsList() { + viewModel.sells.observe(viewLifecycleOwner) { result -> + when (result) { + is ViewState.Success -> { + binding.progressBar.visibility = View.GONE + + if (result.data.isNullOrEmpty()) { + binding.tvEmptyState.visibility = View.VISIBLE + binding.rvSells.visibility = View.GONE + } else { + binding.tvEmptyState.visibility = View.GONE + binding.rvSells.visibility = View.VISIBLE + //sellsAdapter.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 loadSells() { + viewModel.getSellList(status) + } + + private fun navigateToSellsDetail(sells: OrdersItem) { + // In a real app, you would navigate to sells detail screen + // For example: findNavController().navigate(SellsListFragmentDirections.actionToSellsDetail(sells.orderId)) + Toast.makeText(requireContext(), "Order ID: ${sells.orderId}", Toast.LENGTH_SHORT).show() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsPagerAdapter.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsPagerAdapter.kt deleted file mode 100644 index d6fd44a..0000000 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsPagerAdapter.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.alya.ecommerce_serang.ui.profile.mystore.sells - -import androidx.fragment.app.Fragment -import androidx.viewpager2.adapter.FragmentStateAdapter -import com.alya.ecommerce_serang.ui.profile.mystore.sells.all_sells.AllSellsFragment -import com.alya.ecommerce_serang.ui.profile.mystore.sells.cancellation.CancellationFragment -import com.alya.ecommerce_serang.ui.profile.mystore.sells.failed_payment.FailedPaymentFragment -import com.alya.ecommerce_serang.ui.profile.mystore.sells.failed_shipment.FailedShipmentFragment -import com.alya.ecommerce_serang.ui.profile.mystore.sells.completed.CompletedFragment -import com.alya.ecommerce_serang.ui.profile.mystore.sells.order.OrderFragment -import com.alya.ecommerce_serang.ui.profile.mystore.sells.payment.PaymentFragment -import com.alya.ecommerce_serang.ui.profile.mystore.sells.shipment.ShipmentFragment -import com.alya.ecommerce_serang.ui.profile.mystore.sells.shipped.ShippedFragment - -class SellsPagerAdapter(fragment: Fragment, private val itemCount: Int) : - FragmentStateAdapter(fragment) { - - override fun getItemCount(): Int = itemCount - - override fun createFragment(position: Int): Fragment { - return when (position) { - 0 -> AllSellsFragment() - 1 -> OrderFragment() - 2 -> PaymentFragment() - 3 -> ShipmentFragment() - 4 -> ShippedFragment() - 5 -> CompletedFragment() - 6 -> CancellationFragment() - 7 -> FailedPaymentFragment() - 8 -> FailedShipmentFragment() - else -> Fragment() - } - } -} diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsViewPagerAdapter.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsViewPagerAdapter.kt new file mode 100644 index 0000000..d977a7d --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsViewPagerAdapter.kt @@ -0,0 +1,27 @@ +package com.alya.ecommerce_serang.ui.profile.mystore.sells + +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.viewpager2.adapter.FragmentStateAdapter + +class SellsViewPagerAdapter(fragmentActivity: FragmentActivity) + : FragmentStateAdapter(fragmentActivity) { + + private val sellsStatuses = listOf( + "all", // Semua Pesanan + "pending", // Perlu Tagihan + "processed", // Konfirmasi Pembayaran + "paid", // Perlu Dikirim + "shipped", // Dikirim + "delivered", // Dikirim + "completed", // Selesai + "canceled", // Dibatalkan + TODO("Klaim Pembayaran dan Pengajuan Komplain belum ada statusnya") + ) + + override fun getItemCount(): Int = sellsStatuses.size + + override fun createFragment(position: Int): Fragment { + return SellsListFragment.newInstance(sellsStatuses[position]) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/all_sells/AllSellsFragment.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/all_sells/AllSellsFragment.kt deleted file mode 100644 index bee4a1c..0000000 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/all_sells/AllSellsFragment.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.alya.ecommerce_serang.ui.profile.mystore.sells.all_sells - -import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.lifecycle.Observer -import androidx.lifecycle.ViewModelProvider -import androidx.recyclerview.widget.LinearLayoutManager -import com.alya.ecommerce_serang.databinding.FragmentAllSellsBinding -import com.alya.ecommerce_serang.ui.profile.mystore.sells.SellsAdapter -import com.alya.ecommerce_serang.utils.viewmodel.SellsViewModel - -class AllSellsFragment : Fragment() { - private lateinit var viewModel: SellsViewModel - private lateinit var binding: FragmentAllSellsBinding - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - binding = FragmentAllSellsBinding.inflate(inflater, container, false) - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - viewModel = ViewModelProvider(this).get(SellsViewModel::class.java) - - val adapter = SellsAdapter() - binding.rvAllSells.layoutManager = LinearLayoutManager(context) - binding.rvAllSells.adapter = adapter - - viewModel.loadOrdersByStatus("all") - viewModel.sellsList.observe(viewLifecycleOwner, Observer { sells -> - adapter.submitList(sells) - }) - } -} diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/cancellation/CancellationFragment.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/cancellation/CancellationFragment.kt deleted file mode 100644 index c8b93de..0000000 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/cancellation/CancellationFragment.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.alya.ecommerce_serang.ui.profile.mystore.sells.cancellation - -import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.lifecycle.Observer -import androidx.lifecycle.ViewModelProvider -import androidx.recyclerview.widget.LinearLayoutManager -import com.alya.ecommerce_serang.R -import com.alya.ecommerce_serang.databinding.FragmentCancellationBinding -import com.alya.ecommerce_serang.utils.viewmodel.SellsViewModel - -class CancellationFragment : Fragment() { - private lateinit var viewModel: SellsViewModel - private lateinit var binding: FragmentCancellationBinding - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - binding = FragmentCancellationBinding.inflate(inflater, container, false) - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - viewModel = ViewModelProvider(this).get(SellsViewModel::class.java) - -// val adapter = SellsAdapter() -// binding.rvCancellation.layoutManager = LinearLayoutManager(context) -// binding.rvCancellation.adapter = adapter -// -// viewModel.loadOrdersByStatus("cancelled") -// viewModel.sellsList.observe(viewLifecycleOwner, Observer { cancellations -> -// adapter.submitList(cancellations) -// }) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/completed/CompletedFragment.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/completed/CompletedFragment.kt deleted file mode 100644 index a5928b6..0000000 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/completed/CompletedFragment.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.alya.ecommerce_serang.ui.profile.mystore.sells.completed - -import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.lifecycle.Observer -import androidx.lifecycle.ViewModelProvider -import androidx.recyclerview.widget.LinearLayoutManager -import com.alya.ecommerce_serang.R -import com.alya.ecommerce_serang.databinding.FragmentCompletedBinding -import com.alya.ecommerce_serang.utils.viewmodel.SellsViewModel - -class CompletedFragment : Fragment() { - private lateinit var viewModel: SellsViewModel - private lateinit var binding: FragmentCompletedBinding - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - binding = FragmentCompletedBinding.inflate(inflater, container, false) - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - viewModel = ViewModelProvider(this).get(SellsViewModel::class.java) - -// val adapter = SellsAdapter() -// binding.rvCompleted.layoutManager = LinearLayoutManager(context) -// binding.rvCompleted.adapter = adapter -// -// viewModel.loadOrdersByStatus("delivered") -// viewModel.sellsList.observe(viewLifecycleOwner, Observer { completed -> -// adapter.submitList(completed) -// }) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/failed_payment/FailedPaymentFragment.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/failed_payment/FailedPaymentFragment.kt deleted file mode 100644 index eb01dac..0000000 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/failed_payment/FailedPaymentFragment.kt +++ /dev/null @@ -1,42 +0,0 @@ - -package com.alya.ecommerce_serang.ui.profile.mystore.sells.failed_payment - -import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.lifecycle.Observer -import androidx.lifecycle.ViewModelProvider -import androidx.recyclerview.widget.LinearLayoutManager -import com.alya.ecommerce_serang.R -import com.alya.ecommerce_serang.databinding.FragmentFailedPaymentBinding -import com.alya.ecommerce_serang.utils.viewmodel.SellsViewModel - -class FailedPaymentFragment : Fragment() { - private lateinit var viewModel: SellsViewModel - private lateinit var binding: FragmentFailedPaymentBinding - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - binding = FragmentFailedPaymentBinding.inflate(inflater, container, false) - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - viewModel = ViewModelProvider(this).get(SellsViewModel::class.java) - -// val adapter = SellsAdapter() -// binding.rvFailedPayment.layoutManager = LinearLayoutManager(context) -// binding.rvFailedPayment.adapter = adapter -// -// viewModel.loadOrdersByStatus("failedPayment") -// viewModel.sellsList.observe(viewLifecycleOwner, Observer { failedPayments -> -// adapter.submitList(failedPayments) -// }) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/failed_shipment/FailedShipmentFragment.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/failed_shipment/FailedShipmentFragment.kt deleted file mode 100644 index 1e67efa..0000000 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/failed_shipment/FailedShipmentFragment.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.alya.ecommerce_serang.ui.profile.mystore.sells.failed_shipment - -import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.lifecycle.Observer -import androidx.lifecycle.ViewModelProvider -import androidx.recyclerview.widget.LinearLayoutManager -import com.alya.ecommerce_serang.R -import com.alya.ecommerce_serang.databinding.FragmentFailedShipmentBinding -import com.alya.ecommerce_serang.utils.viewmodel.SellsViewModel - -class FailedShipmentFragment : Fragment() { - private lateinit var viewModel: SellsViewModel - private lateinit var binding: FragmentFailedShipmentBinding - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - binding = FragmentFailedShipmentBinding.inflate(inflater, container, false) - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - viewModel = ViewModelProvider(this).get(SellsViewModel::class.java) - -// val adapter = SellsAdapter() -// binding.rvFailedShipment.layoutManager = LinearLayoutManager(context) -// binding.rvFailedShipment.adapter = adapter -// -// viewModel.loadOrdersByStatus("failedShipment") -// viewModel.sellsList.observe(viewLifecycleOwner, Observer { failedShipments -> -// adapter.submitList(failedShipments) -// }) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/order/DetailOrderActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/order/DetailOrderActivity.kt new file mode 100644 index 0000000..1035c14 --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/order/DetailOrderActivity.kt @@ -0,0 +1,21 @@ +package com.alya.ecommerce_serang.ui.profile.mystore.sells.order + +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 DetailOrderActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + setContentView(R.layout.activity_detail_order) + 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/profile/mystore/sells/order/OrderAdapter.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/order/OrderAdapter.kt deleted file mode 100644 index 4cc68e0..0000000 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/order/OrderAdapter.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.alya.ecommerce_serang.ui.profile.mystore.sells.order - -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Button -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.dto.OrdersItem - -class OrderAdapter : RecyclerView.Adapter() { - - private var orderList: List? = emptyList() - - fun submitList(orders: List?) { - orderList = orders - notifyDataSetChanged() - } - - override fun onCreateViewHolder( - parent: ViewGroup, - viewType: Int - ): OrderViewHolder { - val view = LayoutInflater.from(parent.context).inflate(R.layout.item_sells_order, parent, false) - return OrderViewHolder(view) - } - - override fun onBindViewHolder(holder: OrderViewHolder, position: Int) { - val order = orderList?.get(position) - holder.bind(order) - } - - override fun getItemCount(): Int = orderList?.size ?: 0 - - class OrderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - private val tvOrderNumber: TextView = itemView.findViewById(R.id.tv_order_number) - private val tvOrderCustomer: TextView = itemView.findViewById(R.id.tv_order_customer) - private val tvOrderDue: TextView = itemView.findViewById(R.id.tv_order_due) - private val ivOrderProduct: ImageView = itemView.findViewById(R.id.iv_order_product) - private val tvOrderProductName: TextView = itemView.findViewById(R.id.tv_order_product_name) - private val tvOrderProductVariant: TextView = itemView.findViewById(R.id.tv_order_product_variant) - private val tvOrderProductQty: TextView = itemView.findViewById(R.id.tv_order_product_qty) - private val tvOrderProductPrice: TextView = itemView.findViewById(R.id.tv_order_product_price) - private val tvOrderQty: TextView = itemView.findViewById(R.id.tv_order_qty) - private val tvOrderPrice: TextView = itemView.findViewById(R.id.tv_order_price) - private val tvSeeMore: TextView = itemView.findViewById(R.id.tv_see_more) - private val btnEditOrder: Button = itemView.findViewById(R.id.btn_edit_order) - private val btnConfirmOrder: Button = itemView.findViewById(R.id.btn_confirm_order) - - fun bind(order: OrdersItem?) { - tvOrderNumber.text = "No. Pesanan: ${order?.orderId}" - tvOrderCustomer.text = order?.userId.toString() - tvOrderDue.text = order?.createdAt + 7 - tvOrderQty.text = "${order?.orderItems?.size} produk" - tvOrderPrice.text = "Rp${order?.totalAmount}" - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/order/OrderFragment.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/order/OrderFragment.kt deleted file mode 100644 index 60c9de8..0000000 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/order/OrderFragment.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.alya.ecommerce_serang.ui.profile.mystore.sells.order - -import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.lifecycle.Observer -import androidx.lifecycle.ViewModelProvider -import androidx.recyclerview.widget.LinearLayoutManager -import com.alya.ecommerce_serang.databinding.FragmentOrderBinding -import com.alya.ecommerce_serang.utils.viewmodel.SellsViewModel - -class OrderFragment : Fragment() { - private lateinit var viewModel: SellsViewModel - private lateinit var binding: FragmentOrderBinding - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - binding = FragmentOrderBinding.inflate(inflater, container, false) - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - viewModel = ViewModelProvider(this).get(SellsViewModel::class.java) - val adapter = OrderAdapter() - - binding.rvOrder.layoutManager = LinearLayoutManager(context) - binding.rvOrder.adapter = adapter - - viewModel.loadOrdersByStatus("pending") - viewModel.sellsList.observe(viewLifecycleOwner, Observer { orders -> - adapter.submitList(orders) - }) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/payment/ClaimPaymentActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/payment/DetailPaymentActivity.kt similarity index 86% rename from app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/payment/ClaimPaymentActivity.kt rename to app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/payment/DetailPaymentActivity.kt index 5d9613b..8336d08 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/payment/ClaimPaymentActivity.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/payment/DetailPaymentActivity.kt @@ -7,11 +7,11 @@ import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import com.alya.ecommerce_serang.R -class ClaimPaymentActivity : AppCompatActivity() { +class DetailPaymentActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() - setContentView(R.layout.activity_claim_payment) + setContentView(R.layout.activity_detail_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) diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/payment/PaymentAdapter.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/payment/PaymentAdapter.kt deleted file mode 100644 index 030d450..0000000 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/payment/PaymentAdapter.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.alya.ecommerce_serang.ui.profile.mystore.sells.payment - -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Button -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.dto.OrdersItem - -class PaymentAdapter : RecyclerView.Adapter() { - - private var paymentList: List? = emptyList() - - fun submitList(orders: List?) { - paymentList = orders - notifyDataSetChanged() - } - - override fun onCreateViewHolder( - parent: ViewGroup, - viewType: Int - ): PaymentViewHolder { - val view = LayoutInflater.from(parent.context).inflate(R.layout.item_sells_payment, parent, false) - return PaymentViewHolder(view) - } - - override fun onBindViewHolder(holder: PaymentViewHolder, position: Int) { - val order = paymentList?.get(position) - holder.bind(order) - } - - override fun getItemCount(): Int = paymentList?.size ?: 0 - - class PaymentViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){ - private val tvPaymentNumber: TextView = itemView.findViewById(R.id.tv_payment_number) - private val tvPaymentDue: TextView = itemView.findViewById(R.id.tv_payment_due) - private val ivPaymentProduct: ImageView = itemView.findViewById(R.id.iv_payment_product) - private val tvPaymentProductName: TextView = itemView.findViewById(R.id.tv_payment_product_name) - private val tvPaymentProductVariant: TextView = itemView.findViewById(R.id.tv_payment_product_variant) - private val tvPaymentProductQty: TextView = itemView.findViewById(R.id.tv_payment_product_qty) - private val tvPaymentProductPrice: TextView = itemView.findViewById(R.id.tv_payment_product_price) - private val tvPaymentQty: TextView = itemView.findViewById(R.id.tv_payment_qty) - private val tvPaymentPrice: TextView = itemView.findViewById(R.id.tv_payment_price) - private val tvPaymentCustomer: TextView = itemView.findViewById(R.id.tv_payment_customer) - private val tvPaymentLocation: TextView = itemView.findViewById(R.id.tv_payment_location) - private val tvSeeMore: TextView = itemView.findViewById(R.id.tv_see_more) - private val btnConfirmPayment: Button = itemView.findViewById(R.id.btn_confirm_payment) - - fun bind(order: OrdersItem?) { - tvPaymentNumber.text = "No. Pesanan: ${order?.orderId}" - tvPaymentDue.text = order?.createdAt + 7 - tvPaymentQty.text = "${order?.orderItems?.size} produk" - tvPaymentPrice.text = "Rp${order?.totalAmount}" - tvPaymentCustomer.text = order?.userId.toString() - tvPaymentLocation.text = order?.addressId.toString() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/payment/PaymentFragment.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/payment/PaymentFragment.kt deleted file mode 100644 index 7c5214c..0000000 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/payment/PaymentFragment.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.alya.ecommerce_serang.ui.profile.mystore.sells.payment - -import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.lifecycle.Observer -import androidx.lifecycle.ViewModelProvider -import androidx.recyclerview.widget.LinearLayoutManager -import com.alya.ecommerce_serang.databinding.FragmentPaymentBinding -import com.alya.ecommerce_serang.utils.viewmodel.SellsViewModel - -class PaymentFragment : Fragment() { - private lateinit var viewModel: SellsViewModel - private lateinit var binding: FragmentPaymentBinding - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - binding = FragmentPaymentBinding.inflate(inflater, container, false) - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - viewModel = ViewModelProvider(this).get(SellsViewModel::class.java) - val adapter = PaymentAdapter() - - binding.rvPayment.layoutManager = LinearLayoutManager(context) - binding.rvPayment.adapter = adapter - - viewModel.loadOrdersByStatus("paid") - viewModel.sellsList.observe(viewLifecycleOwner, Observer { payments -> - adapter.submitList(payments) - }) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipment/ShippingConfirmationActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipment/DetailShipmentActivity.kt similarity index 85% rename from app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipment/ShippingConfirmationActivity.kt rename to app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipment/DetailShipmentActivity.kt index 94b9c67..89e1b54 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipment/ShippingConfirmationActivity.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipment/DetailShipmentActivity.kt @@ -7,11 +7,11 @@ import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import com.alya.ecommerce_serang.R -class ShippingConfirmationActivity : AppCompatActivity() { +class DetailShipmentActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() - setContentView(R.layout.activity_shipping_confirmation) + setContentView(R.layout.activity_detail_shipment) 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) diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipment/ShipmentAdapter.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipment/ShipmentAdapter.kt deleted file mode 100644 index d7e4fa0..0000000 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipment/ShipmentAdapter.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.alya.ecommerce_serang.ui.profile.mystore.sells.shipment - -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Button -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.dto.OrdersItem - -class ShipmentAdapter : RecyclerView.Adapter() { - - private var shipmentList: List = emptyList() - - fun submitList(orders: List) { - shipmentList = orders - notifyDataSetChanged() - } - - override fun onCreateViewHolder( - parent: ViewGroup, - viewType: Int - ): ShipmentAdapter.ShipmentViewHolder { - val view = LayoutInflater.from(parent.context).inflate(R.layout.item_sells_shipment, parent, false) - return ShipmentViewHolder(view) - } - - override fun onBindViewHolder(holder: ShipmentAdapter.ShipmentViewHolder, position: Int) { - val order = shipmentList[position] - holder.bind(order) - } - - override fun getItemCount(): Int = shipmentList.size - - class ShipmentViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - private val tvShipmentNumber: TextView = itemView.findViewById(R.id.tv_shipment_number) - private val tvShipmentDue: TextView = itemView.findViewById(R.id.tv_shipment_due) - private val ivShipmentProduct: ImageView = itemView.findViewById(R.id.iv_shipment_product) - private val tvShipmentProductName: TextView = itemView.findViewById(R.id.tv_shipment_product_name) - private val tvShipmentProductVariant: TextView = itemView.findViewById(R.id.tv_shipment_product_variant) - private val tvShipmentProductQty: TextView = itemView.findViewById(R.id.tv_shipment_product_qty) - private val tvShipmentCustomer: TextView = itemView.findViewById(R.id.tv_shipment_customer) - private val tvShipmentLocation: TextView = itemView.findViewById(R.id.tv_shipment_location) - private val tvSeeMore: TextView = itemView.findViewById(R.id.tv_see_more) - private val btnConfirmPayment: Button = itemView.findViewById(R.id.btn_confirm_payment) - - fun bind(order: OrdersItem) { - tvShipmentNumber.text = "No. Pesanan: ${order.orderId}" - tvShipmentDue.text = order.createdAt + 7 - tvShipmentCustomer.text = order.userId.toString() - tvShipmentLocation.text = order.addressId.toString() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipment/ShipmentFragment.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipment/ShipmentFragment.kt deleted file mode 100644 index 4b0f1d8..0000000 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipment/ShipmentFragment.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.alya.ecommerce_serang.ui.profile.mystore.sells.shipment - -import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.lifecycle.Observer -import androidx.lifecycle.ViewModelProvider -import androidx.recyclerview.widget.LinearLayoutManager -import com.alya.ecommerce_serang.R -import com.alya.ecommerce_serang.databinding.FragmentShipmentBinding -import com.alya.ecommerce_serang.utils.viewmodel.SellsViewModel - -class ShipmentFragment : Fragment() { - private lateinit var viewModel: SellsViewModel - private lateinit var binding: FragmentShipmentBinding - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - binding = FragmentShipmentBinding.inflate(inflater, container, false) - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - viewModel = ViewModelProvider(this).get(SellsViewModel::class.java) - val adapter = ShipmentAdapter() - - binding.rvShipment.layoutManager = LinearLayoutManager(context) -// binding.rvShipment.adapter = adapter -// -// viewModel.loadOrdersByStatus("processed") -// viewModel.sellsList.observe(viewLifecycleOwner, Observer { shipments -> -// adapter.submitList(shipments) -// }) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipped/ShippedFragment.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipped/ShippedFragment.kt deleted file mode 100644 index 379d9d9..0000000 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipped/ShippedFragment.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.alya.ecommerce_serang.ui.profile.mystore.sells.shipped - -import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.lifecycle.Observer -import androidx.lifecycle.ViewModelProvider -import androidx.recyclerview.widget.LinearLayoutManager -import com.alya.ecommerce_serang.R -import com.alya.ecommerce_serang.databinding.FragmentShippedBinding -import com.alya.ecommerce_serang.utils.viewmodel.SellsViewModel - -class ShippedFragment : Fragment() { - private lateinit var viewModel: SellsViewModel - private lateinit var binding: FragmentShippedBinding - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - binding = FragmentShippedBinding.inflate(inflater, container, false) - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - viewModel = ViewModelProvider(this).get(SellsViewModel::class.java) - -// val adapter = SellsAdapter() -// binding.rvShipped.layoutManager = LinearLayoutManager(context) -// binding.rvShipped.adapter = adapter -// -// viewModel.loadOrdersByStatus("shipped") -// viewModel.sellsList.observe(viewLifecycleOwner, Observer { shipped -> -// adapter.submitList(shipped) -// }) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/SellsViewModel.kt b/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/SellsViewModel.kt index a7d46bc..ee3aa28 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/SellsViewModel.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/SellsViewModel.kt @@ -1,26 +1,61 @@ package com.alya.ecommerce_serang.utils.viewmodel +import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel -import androidx.lifecycle.liveData import androidx.lifecycle.viewModelScope -import com.alya.ecommerce_serang.data.api.dto.OrdersItem -import com.alya.ecommerce_serang.data.repository.OrderRepository +import com.alya.ecommerce_serang.data.api.response.store.orders.OrdersItem +import com.alya.ecommerce_serang.data.repository.Result +import com.alya.ecommerce_serang.data.repository.SellsRepository +import com.alya.ecommerce_serang.ui.order.address.ViewState import kotlinx.coroutines.launch -class SellsViewModel(private val repository: OrderRepository) : ViewModel() { - private val _sellsList = MutableLiveData>() - val sellsList: LiveData> get() = _sellsList +class SellsViewModel(private val repository: SellsRepository) : ViewModel() { + + companion object { + private const val TAG = "SellsViewModel" + } + + private val _sells = MutableLiveData?>>() + val sells: LiveData?>> = _sells + + fun getSellList(status: String) { + _sells.value = ViewState.Loading - fun loadOrdersByStatus(status: String) { viewModelScope.launch { - val result = if (status == "all") { - repository.fetchSells() - } else { - repository.fetchOrdersByStatus(status) + _sells.value = ViewState.Loading + + try { + when (val result = repository.getSellList(status)) { + is Result.Success -> { + _sells.value = ViewState.Success(result.data.orders) + Log.d("SellsViewModel", "Sells loaded successfully: ${result.data.orders?.size} items") + } + is Result.Error -> { + _sells.value = ViewState.Error(result.exception.message ?: "Unknown error occurred") + Log.e("SellsViewModel", "Error loading sells", result.exception) + } + is Result.Loading -> { + null + } + } + } catch (e: Exception) { + _sells.value = ViewState.Error("An unexpected error occurred: ${e.message}") + Log.e("SellsViewModel", "Exception in getOrderList", e) + } + } + } + + fun updateOrderStatus(orderId: Int?, status: String) { + Log.d(TAG, "Updating order status: orderId=$orderId, status=$status") + viewModelScope.launch { + try { + repository.updateOrderStatus(orderId, status) + Log.d(TAG, "Order status updated successfully: orderId=$orderId, status=$status") + } catch (e: Exception) { + Log.e(TAG, "Error updating order status", e) } - _sellsList.value = result } } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_detail_order.xml b/app/src/main/res/layout/activity_detail_order.xml new file mode 100644 index 0000000..200764e --- /dev/null +++ b/app/src/main/res/layout/activity_detail_order.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_detail_payment.xml b/app/src/main/res/layout/activity_detail_payment.xml new file mode 100644 index 0000000..dc98fec --- /dev/null +++ b/app/src/main/res/layout/activity_detail_payment.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_detail_shipment.xml b/app/src/main/res/layout/activity_detail_shipment.xml new file mode 100644 index 0000000..2aca87f --- /dev/null +++ b/app/src/main/res/layout/activity_detail_shipment.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_sells.xml b/app/src/main/res/layout/activity_sells.xml index 8056d96..c3b72d5 100644 --- a/app/src/main/res/layout/activity_sells.xml +++ b/app/src/main/res/layout/activity_sells.xml @@ -1,14 +1,55 @@ - - + - \ No newline at end of file + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_all_sells.xml b/app/src/main/res/layout/fragment_all_sells.xml deleted file mode 100644 index dc03543..0000000 --- a/app/src/main/res/layout/fragment_all_sells.xml +++ /dev/null @@ -1,6 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_cancellation.xml b/app/src/main/res/layout/fragment_cancellation.xml deleted file mode 100644 index 86c48e5..0000000 --- a/app/src/main/res/layout/fragment_cancellation.xml +++ /dev/null @@ -1,7 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_completed.xml b/app/src/main/res/layout/fragment_completed.xml deleted file mode 100644 index fc5c715..0000000 --- a/app/src/main/res/layout/fragment_completed.xml +++ /dev/null @@ -1,7 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_failed_payment.xml b/app/src/main/res/layout/fragment_failed_payment.xml deleted file mode 100644 index 5587090..0000000 --- a/app/src/main/res/layout/fragment_failed_payment.xml +++ /dev/null @@ -1,7 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_failed_shipment.xml b/app/src/main/res/layout/fragment_failed_shipment.xml deleted file mode 100644 index 0707558..0000000 --- a/app/src/main/res/layout/fragment_failed_shipment.xml +++ /dev/null @@ -1,7 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_order.xml b/app/src/main/res/layout/fragment_order.xml deleted file mode 100644 index 6accc4e..0000000 --- a/app/src/main/res/layout/fragment_order.xml +++ /dev/null @@ -1,7 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_order_history.xml b/app/src/main/res/layout/fragment_order_history.xml index e1221d7..aab1265 100644 --- a/app/src/main/res/layout/fragment_order_history.xml +++ b/app/src/main/res/layout/fragment_order_history.xml @@ -11,10 +11,12 @@ android:background="@android:color/white" app:layout_constraintTop_toTopOf="parent" app:tabGravity="fill" - app:tabIndicatorColor="@color/blue_200" app:tabMode="scrollable" - app:tabSelectedTextColor="@color/blue_200" - app:tabTextColor="@android:color/darker_gray" /> + app:tabTextAppearance="@style/label_medium_prominent" + app:tabSelectedTextAppearance="@style/label_medium_prominent" + app:tabIndicatorColor="@color/blue_500" + app:tabSelectedTextColor="@color/blue_500" + app:tabTextColor="@color/black_300" /> - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_sells_list.xml b/app/src/main/res/layout/fragment_sells_list.xml new file mode 100644 index 0000000..155e392 --- /dev/null +++ b/app/src/main/res/layout/fragment_sells_list.xml @@ -0,0 +1,38 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_shipment.xml b/app/src/main/res/layout/fragment_shipment.xml deleted file mode 100644 index 4b2902e..0000000 --- a/app/src/main/res/layout/fragment_shipment.xml +++ /dev/null @@ -1,7 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_shipped.xml b/app/src/main/res/layout/fragment_shipped.xml deleted file mode 100644 index 907ec43..0000000 --- a/app/src/main/res/layout/fragment_shipped.xml +++ /dev/null @@ -1,7 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_sells_order.xml b/app/src/main/res/layout/item_sells_order.xml index 8cc6547..3a2f158 100644 --- a/app/src/main/res/layout/item_sells_order.xml +++ b/app/src/main/res/layout/item_sells_order.xml @@ -16,7 +16,7 @@ app:layout_constraintTop_toTopOf="parent"/> @@ -113,13 +113,13 @@ - + + + + + + + + @@ -163,12 +163,12 @@ android:orientation="vertical" android:layout_marginStart="13dp" android:gravity="end" - app:layout_constraintStart_toEndOf="@id/iv_order_product" + app:layout_constraintStart_toEndOf="@id/iv_sells_product" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintBottom_toBottomOf="@id/iv_order_product"> + app:layout_constraintBottom_toBottomOf="@id/iv_sells_product"> + app:layout_constraintTop_toBottomOf="@id/layout_sells_detail"/> \ No newline at end of file diff --git a/app/src/main/res/layout/item_sells_payment.xml b/app/src/main/res/layout/item_sells_payment.xml index acaf5f8..90569b1 100644 --- a/app/src/main/res/layout/item_sells_payment.xml +++ b/app/src/main/res/layout/item_sells_payment.xml @@ -16,7 +16,7 @@ app:layout_constraintTop_toTopOf="parent"/> @@ -91,13 +92,13 @@ - + + + + + + + + @@ -141,12 +142,12 @@ android:orientation="vertical" android:layout_marginStart="13dp" android:gravity="end" - app:layout_constraintStart_toEndOf="@id/iv_payment_product" + app:layout_constraintStart_toEndOf="@id/iv_sells_product" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintBottom_toBottomOf="@id/iv_payment_product"> + app:layout_constraintBottom_toBottomOf="@id/iv_sells_product"> + app:layout_constraintTop_toBottomOf="@id/layout_sells_detail"/> \ No newline at end of file diff --git a/app/src/main/res/layout/item_sells_shipment.xml b/app/src/main/res/layout/item_sells_shipment.xml index 40db7ee..7be7057 100644 --- a/app/src/main/res/layout/item_sells_shipment.xml +++ b/app/src/main/res/layout/item_sells_shipment.xml @@ -16,7 +16,7 @@ app:layout_constraintTop_toTopOf="parent"/> @@ -91,13 +91,13 @@ - + + + + + + + + + app:layout_constraintBottom_toBottomOf="@id/iv_sells_product"/> + app:layout_constraintTop_toBottomOf="@id/layout_sells_detail"/> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 36c0bcf..79bcdc6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -10,7 +10,7 @@ Rp%.1f Coba lagi\n - Terdapat error... + Terdapat error… Produk Terbaru 4.5 Buka Toko From 4497300d1263f672aa8af5559017d8ee8e22f621 Mon Sep 17 00:00:00 2001 From: Gracia Hotmauli <95269134+hotmauligracia@users.noreply.github.com> Date: Mon, 12 May 2025 16:50:31 +0700 Subject: [PATCH 2/4] edit store profile & topup --- app/src/main/AndroidManifest.xml | 6 +- .../ecommerce_serang/data/api/dto/City.kt | 31 ++ .../ecommerce_serang/data/api/dto/Province.kt | 19 + .../data/api/dto/StoreAddress.kt | 55 +++ .../store/profile/StoreDataResponse.kt | 55 +++ .../store/topup/BalanceTopUpResponse.kt | 6 + .../data/api/retrofit/ApiService.kt | 55 +++ .../data/repository/AddressRepository.kt | 174 ++++++++ .../mystore/balance/BalanceActivity.kt | 17 +- .../mystore/balance/BalanceTopUpActivity.kt | 396 +++++++++++++++++- .../profile/DetailStoreProfileActivity.kt | 62 ++- .../profile/EditStoreProfileActivity.kt | 304 ++++++++++++++ .../address/DetailStoreAddressActivity.kt | 311 +++++++++++++- .../utils/viewmodel/AddressViewModel.kt | 129 ++++++ .../res/layout/activity_balance_top_up.xml | 3 +- .../layout/activity_detail_store_address.xml | 219 +++++----- .../layout/activity_detail_store_product.xml | 3 +- .../layout/activity_detail_store_profile.xml | 4 +- .../layout/activity_edit_store_profile.xml | 183 ++++++++ gradle/libs.versions.toml | 2 +- 20 files changed, 1906 insertions(+), 128 deletions(-) create mode 100644 app/src/main/java/com/alya/ecommerce_serang/data/api/dto/City.kt create mode 100644 app/src/main/java/com/alya/ecommerce_serang/data/api/dto/Province.kt create mode 100644 app/src/main/java/com/alya/ecommerce_serang/data/api/dto/StoreAddress.kt create mode 100644 app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/profile/StoreDataResponse.kt create mode 100644 app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/topup/BalanceTopUpResponse.kt create mode 100644 app/src/main/java/com/alya/ecommerce_serang/data/repository/AddressRepository.kt create mode 100644 app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/EditStoreProfileActivity.kt create mode 100644 app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/AddressViewModel.kt create mode 100644 app/src/main/res/layout/activity_edit_store_profile.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 20052e6..fa26332 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"> + @@ -40,8 +43,7 @@ android:exported="false" /> - + android:exported="false" /> diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/City.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/City.kt new file mode 100644 index 0000000..f8160a2 --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/City.kt @@ -0,0 +1,31 @@ +package com.alya.ecommerce_serang.data.api.dto + +import com.google.gson.annotations.SerializedName + +data class City( + @SerializedName("city_id") + val cityId: String, + + @SerializedName("city_name") + val cityName: String, + + @SerializedName("province_id") + val provinceId: String, + + @SerializedName("province") + val provinceName: String, + + @SerializedName("type") + val type: String, + + @SerializedName("postal_code") + val postalCode: String +) + +data class CityResponse( + @SerializedName("message") + val message: String, + + @SerializedName("cities") + val data: List +) \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/Province.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/Province.kt new file mode 100644 index 0000000..056d517 --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/Province.kt @@ -0,0 +1,19 @@ +package com.alya.ecommerce_serang.data.api.dto + +import com.google.gson.annotations.SerializedName + +data class Province( + @SerializedName("province_id") + val provinceId: String, + + @SerializedName("province") + val provinceName: String +) + +data class ProvinceResponse( + @SerializedName("message") + val message: String, + + @SerializedName("provinces") + val data: List +) \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/StoreAddress.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/StoreAddress.kt new file mode 100644 index 0000000..f403fc2 --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/StoreAddress.kt @@ -0,0 +1,55 @@ +package com.alya.ecommerce_serang.data.api.dto + +import com.google.gson.annotations.SerializedName + +data class StoreAddress( + @SerializedName("id") + val id: String? = null, + + @SerializedName("store_id") + val storeId: String? = null, + + @SerializedName("province_id") + val provinceId: String = "", + + @SerializedName("province_name") + val provinceName: String = "", + + @SerializedName("city_id") + val cityId: String = "", + + @SerializedName("city_name") + val cityName: String = "", + + @SerializedName("street") + val street: String = "", + + @SerializedName("subdistrict") + val subdistrict: String = "", + + @SerializedName("detail") + val detail: String? = null, + + @SerializedName("postal_code") + val postalCode: String = "", + + @SerializedName("latitude") + val latitude: Double? = 0.0, + + @SerializedName("longitude") + val longitude: Double? = 0.0, + + @SerializedName("created_at") + val createdAt: String? = null, + + @SerializedName("updated_at") + val updatedAt: String? = null +) + +data class StoreAddressResponse( + @SerializedName("message") + val message: String, + + @SerializedName("store") + val data: StoreAddress? = null +) \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/profile/StoreDataResponse.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/profile/StoreDataResponse.kt new file mode 100644 index 0000000..711e8be --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/profile/StoreDataResponse.kt @@ -0,0 +1,55 @@ +package com.alya.ecommerce_serang.data.api.response.store.profile + +import com.google.gson.annotations.SerializedName + +data class StoreDataResponse( + val message: String, + val store: Store, + val shipping: List, + val payment: List +) + +data class Store( + @SerializedName("store_id") val storeId: Int, + @SerializedName("store_status") val storeStatus: String, + @SerializedName("store_name") val storeName: String, + @SerializedName("user_name") val userName: String, + val email: String, + @SerializedName("user_phone") val userPhone: String, + val balance: String, + val ktp: String, + val npwp: String, + val nib: String, + val persetujuan: String?, + @SerializedName("store_image") val storeImage: String, + @SerializedName("store_description") val storeDescription: String, + @SerializedName("is_on_leave") val isOnLeave: Boolean, + @SerializedName("store_type_id") val storeTypeId: Int, + @SerializedName("store_type") val storeType: String, + val id: Int, + val latitude: String, + val longitude: String, + val street: String, + val subdistrict: String, + @SerializedName("postal_code") val postalCode: String, + val detail: String, + @SerializedName("is_store_location") val isStoreLocation: Boolean, + @SerializedName("user_id") val userId: Int, + @SerializedName("city_id") val cityId: Int, + @SerializedName("province_id") val provinceId: Int, + val phone: String?, + val recipient: String?, + @SerializedName("approval_status") val approvalStatus: String, + @SerializedName("approval_reason") val approvalReason: String? +) + +data class Shipping( + val courier: String +) + +data class Payment( + val id: Int, + @SerializedName("bank_num") val bankNum: String, + @SerializedName("bank_name") val bankName: String, + @SerializedName("qris_image") val qrisImage: String +) diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/topup/BalanceTopUpResponse.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/topup/BalanceTopUpResponse.kt new file mode 100644 index 0000000..a6965ce --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/topup/BalanceTopUpResponse.kt @@ -0,0 +1,6 @@ +package com.alya.ecommerce_serang.data.api.response.store.topup + +data class BalanceTopUpResponse( + val success: Boolean, + val message: String +) 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 62a4ab0..b86b350 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 @@ -3,6 +3,7 @@ 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.CityResponse import com.alya.ecommerce_serang.data.api.dto.CompletedOrderRequest import com.alya.ecommerce_serang.data.api.dto.CourierCostRequest import com.alya.ecommerce_serang.data.api.dto.CreateAddressRequest @@ -10,8 +11,10 @@ import com.alya.ecommerce_serang.data.api.dto.LoginRequest 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.OtpRequest +import com.alya.ecommerce_serang.data.api.dto.ProvinceResponse import com.alya.ecommerce_serang.data.api.dto.RegisterRequest import com.alya.ecommerce_serang.data.api.dto.SearchRequest +import com.alya.ecommerce_serang.data.api.dto.StoreAddressResponse import com.alya.ecommerce_serang.data.api.dto.UpdateCart import com.alya.ecommerce_serang.data.api.dto.UpdateChatRequest import com.alya.ecommerce_serang.data.api.response.auth.LoginResponse @@ -48,6 +51,8 @@ import com.alya.ecommerce_serang.data.api.response.store.product.CreateProductRe import com.alya.ecommerce_serang.data.api.response.store.product.DeleteProductResponse import com.alya.ecommerce_serang.data.api.response.store.product.UpdateProductResponse import com.alya.ecommerce_serang.data.api.response.store.product.ViewStoreProductsResponse +import com.alya.ecommerce_serang.data.api.response.store.profile.StoreDataResponse +import com.alya.ecommerce_serang.data.api.response.store.topup.BalanceTopUpResponse import okhttp3.MultipartBody import okhttp3.RequestBody import retrofit2.Call @@ -147,6 +152,8 @@ interface ApiService { @GET("mystore") suspend fun getStore (): Response + suspend fun getStoreData(): Response + suspend fun getStoreAddress(): Response @GET("mystore/product") // Replace with actual endpoint suspend fun getStoreProduct(): Response @@ -235,6 +242,54 @@ interface ApiService { @Part complaintimg: MultipartBody.Part ): Response + @Multipart + @POST("store/createtopup") + suspend fun addBalanceTopUp( + @Part topupimg: MultipartBody.Part, + @Part("amount") amount: RequestBody, + @Part("payment_info_id") paymentInfoId: RequestBody, + @Part("transaction_date") transactionDate: RequestBody, + @Part("bank_name") bankName: RequestBody, + @Part("bank_num") bankNum: RequestBody + ): Response + + @PUT("mystore/edit") + suspend fun updateStoreProfile( + @Body requestBody: okhttp3.RequestBody + ): Response + + @Multipart + @PUT("mystore/edit") + suspend fun updateStoreProfileMultipart( + @Part("store_name") storeName: RequestBody, + @Part("store_status") storeStatus: RequestBody, + @Part("store_description") storeDescription: RequestBody, + @Part("is_on_leave") isOnLeave: RequestBody, + @Part("city_id") cityId: RequestBody, + @Part("province_id") provinceId: RequestBody, + @Part("street") street: RequestBody, + @Part("subdistrict") subdistrict: RequestBody, + @Part("detail") detail: RequestBody, + @Part("postal_code") postalCode: RequestBody, + @Part("latitude") latitude: RequestBody, + @Part("longitude") longitude: RequestBody, + @Part("user_phone") userPhone: RequestBody, + @Part storeimg: MultipartBody.Part? + ): Response + + @GET("provinces") + suspend fun getProvinces(): Response + + @GET("cities/{provinceId}") + suspend fun getCities( + @Path("provinceId") provinceId: String + ): Response + + @PUT("mystore/edit") + suspend fun updateStoreAddress( + @Body addressData: HashMap + ): Response + @POST("search") suspend fun saveSearchQuery( @Body searchRequest: SearchRequest diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/repository/AddressRepository.kt b/app/src/main/java/com/alya/ecommerce_serang/data/repository/AddressRepository.kt new file mode 100644 index 0000000..8659ef7 --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/data/repository/AddressRepository.kt @@ -0,0 +1,174 @@ +package com.alya.ecommerce_serang.data.repository + +import android.util.Log +import com.alya.ecommerce_serang.data.api.dto.City +import com.alya.ecommerce_serang.data.api.dto.Province +import com.alya.ecommerce_serang.data.api.dto.StoreAddress +import com.alya.ecommerce_serang.data.api.retrofit.ApiService +import com.google.gson.Gson +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.json.JSONObject + +class AddressRepository(private val apiService: ApiService) { + + private val TAG = "AddressRepository" + + suspend fun getProvinces(): List = withContext(Dispatchers.IO) { + Log.d(TAG, "getProvinces() called") + try { + val response = apiService.getProvinces() + Log.d(TAG, "getProvinces() response: isSuccessful=${response.isSuccessful}, code=${response.code()}") + + // Log the raw response body for debugging + val rawBody = response.raw().toString() + Log.d(TAG, "Raw response: $rawBody") + + if (response.isSuccessful) { + val responseBody = response.body() + Log.d(TAG, "Response body: ${Gson().toJson(responseBody)}") + + val provinces = responseBody?.data ?: emptyList() + Log.d(TAG, "getProvinces() success, got ${provinces.size} provinces") + return@withContext provinces + } else { + val errorBody = response.errorBody()?.string() ?: "Unknown error" + Log.e(TAG, "getProvinces() error: $errorBody") + throw Exception("API Error (${response.code()}): $errorBody") + } + } catch (e: Exception) { + Log.e(TAG, "Exception in getProvinces()", e) + throw Exception("Network error: ${e.message}") + } + } + + suspend fun getCities(provinceId: String): List = withContext(Dispatchers.IO) { + Log.d(TAG, "getCities() called with provinceId: $provinceId") + try { + val response = apiService.getCities(provinceId) + Log.d(TAG, "getCities() response: isSuccessful=${response.isSuccessful}, code=${response.code()}") + + if (response.isSuccessful) { + val responseBody = response.body() + Log.d(TAG, "Response body: ${Gson().toJson(responseBody)}") + + val cities = responseBody?.data ?: emptyList() + Log.d(TAG, "getCities() success, got ${cities.size} cities") + return@withContext cities + } else { + val errorBody = response.errorBody()?.string() ?: "Unknown error" + Log.e(TAG, "getCities() error: $errorBody") + throw Exception("API Error (${response.code()}): $errorBody") + } + } catch (e: Exception) { + Log.e(TAG, "Exception in getCities()", e) + throw Exception("Network error: ${e.message}") + } + } + + suspend fun getStoreAddress(): StoreAddress? = withContext(Dispatchers.IO) { + Log.d(TAG, "getStoreAddress() called") + try { + val response = apiService.getStoreAddress() + Log.d(TAG, "getStoreAddress() response: isSuccessful=${response.isSuccessful}, code=${response.code()}") + + if (response.isSuccessful) { + val responseBody = response.body() + val rawJson = Gson().toJson(responseBody) + Log.d(TAG, "Response body: $rawJson") + + val address = responseBody?.data + Log.d(TAG, "getStoreAddress() success, address: $address") + + // Convert numeric strings to proper types if needed + address?.let { + // Handle city_id if it's a number + if (it.cityId.isBlank() && rawJson.contains("city_id")) { + try { + val cityId = JSONObject(rawJson).getJSONObject("store").optInt("city_id", 0) + if (cityId > 0) { + it.javaClass.getDeclaredField("cityId").apply { + isAccessible = true + set(it, cityId.toString()) + } + Log.d(TAG, "Updated cityId to: ${it.cityId}") + } + } catch (e: Exception) { + Log.e(TAG, "Error parsing city_id", e) + } + } + + // Handle province_id if it's a number + if (it.provinceId.isBlank() && rawJson.contains("province_id")) { + try { + val provinceId = JSONObject(rawJson).getJSONObject("store").optInt("province_id", 0) + if (provinceId > 0) { + it.javaClass.getDeclaredField("provinceId").apply { + isAccessible = true + set(it, provinceId.toString()) + } + Log.d(TAG, "Updated provinceId to: ${it.provinceId}") + } + } catch (e: Exception) { + Log.e(TAG, "Error parsing province_id", e) + } + } + } + + return@withContext address + } else { + val errorBody = response.errorBody()?.string() ?: "Unknown error" + Log.e(TAG, "getStoreAddress() error: $errorBody") + throw Exception("API Error (${response.code()}): $errorBody") + } + } catch (e: Exception) { + Log.e(TAG, "Exception in getStoreAddress()", e) + throw Exception("Network error: ${e.message}") + } + } + + suspend fun saveStoreAddress( + provinceId: String, + provinceName: String, + cityId: String, + cityName: String, + street: String, + subdistrict: String, + detail: String, + postalCode: String, + latitude: Double, + longitude: Double + ): Boolean = withContext(Dispatchers.IO) { + Log.d(TAG, "saveStoreAddress() called with provinceId: $provinceId, cityId: $cityId") + + try { + val addressMap = HashMap() + addressMap["provinceId"] = provinceId + addressMap["provinceName"] = provinceName + addressMap["cityId"] = cityId + addressMap["cityName"] = cityName + addressMap["street"] = street + addressMap["subdistrict"] = subdistrict + addressMap["detail"] = detail + addressMap["postalCode"] = postalCode + addressMap["latitude"] = latitude + addressMap["longitude"] = longitude + + Log.d(TAG, "saveStoreAddress() request data: $addressMap") + val response = apiService.updateStoreAddress(addressMap) + Log.d(TAG, "saveStoreAddress() response: isSuccessful=${response.isSuccessful}, code=${response.code()}") + + if (response.isSuccessful) { + Log.d(TAG, "saveStoreAddress() success") + return@withContext true + } else { + val errorBody = response.errorBody()?.string() ?: "Unknown error" + Log.e(TAG, "saveStoreAddress() error: $errorBody") + throw Exception("API Error (${response.code()}): $errorBody") + } + } catch (e: Exception) { + Log.e(TAG, "Exception in saveStoreAddress()", e) + throw Exception("Network error: ${e.message}") + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/balance/BalanceActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/balance/BalanceActivity.kt index 4192943..24e9618 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/balance/BalanceActivity.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/balance/BalanceActivity.kt @@ -1,21 +1,36 @@ package com.alya.ecommerce_serang.ui.profile.mystore.balance +import android.content.Intent 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 +import com.alya.ecommerce_serang.databinding.ActivityBalanceBinding class BalanceActivity : AppCompatActivity() { + private lateinit var binding: ActivityBalanceBinding + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() - setContentView(R.layout.activity_balance) + binding = ActivityBalanceBinding.inflate(layoutInflater) + setContentView(binding.root) + 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 } + + setupListeners() + } + + private fun setupListeners() { + binding.btnTopUp.setOnClickListener { + val intent = Intent(this, BalanceTopUpActivity::class.java) + startActivity(intent) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/balance/BalanceTopUpActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/balance/BalanceTopUpActivity.kt index 12c437a..5ef8b31 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/balance/BalanceTopUpActivity.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/balance/BalanceTopUpActivity.kt @@ -1,21 +1,395 @@ package com.alya.ecommerce_serang.ui.profile.mystore.balance +import android.app.DatePickerDialog +import android.content.Intent +import android.net.Uri import android.os.Bundle -import androidx.activity.enableEdgeToEdge +import android.provider.MediaStore +import android.view.View +import android.widget.AdapterView +import android.widget.ArrayAdapter +import android.widget.Button +import android.widget.EditText +import android.widget.ImageView +import android.widget.Spinner +import android.widget.TextView +import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat +import androidx.lifecycle.lifecycleScope import com.alya.ecommerce_serang.R +import com.alya.ecommerce_serang.data.api.response.store.profile.Payment +import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig +import com.alya.ecommerce_serang.utils.SessionManager +import kotlinx.coroutines.launch +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody.Companion.asRequestBody +import okhttp3.RequestBody.Companion.toRequestBody +import java.io.File +import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Locale class BalanceTopUpActivity : AppCompatActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - enableEdgeToEdge() - setContentView(R.layout.activity_balance_top_up) - 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 + private lateinit var imgPreview: ImageView + private lateinit var addPhotoTextView: TextView + private lateinit var edtNominal: EditText + private lateinit var spinnerPaymentMethod: Spinner + private lateinit var edtTransactionDate: EditText + private lateinit var datePickerIcon: ImageView + private lateinit var btnSend: Button + private lateinit var sessionManager: SessionManager + + private var selectedImageUri: Uri? = null + private var paymentMethods: List = emptyList() + private var selectedPaymentId: Int = -1 + + private val calendar = Calendar.getInstance() + + private val getImageContent = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == RESULT_OK) { + val imageUri = result.data?.data + imageUri?.let { + selectedImageUri = it + imgPreview.setImageURI(it) + } } } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_balance_top_up) + + // Initialize session manager + sessionManager = SessionManager(this) + + // Initialize views + imgPreview = findViewById(R.id.img_preview) + addPhotoTextView = findViewById(R.id.tv_tambah_foto) + edtNominal = findViewById(R.id.edt_nominal_topup) + spinnerPaymentMethod = findViewById(R.id.spinner_metode_bayar) + edtTransactionDate = findViewById(R.id.edt_tgl_transaksi) + datePickerIcon = findViewById(R.id.img_date_picker) + btnSend = findViewById(R.id.btn_send) + + // Setup header title + val headerTitle = findViewById(R.id.header_title) + headerTitle.text = "Isi Ulang Saldo" + + // Setup back button + val backButton = findViewById(R.id.header_left_icon) + backButton.setOnClickListener { + finish() + } + + // Setup photo selection + addPhotoTextView.setOnClickListener { + openGallery() + } + + imgPreview.setOnClickListener { + openGallery() + } + + // Setup date picker + setupDatePicker() + + // Fetch payment methods + fetchPaymentMethods() + + // Setup submit button + btnSend.setOnClickListener { + submitForm() + } + } + + private fun openGallery() { + val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI) + getImageContent.launch(intent) + } + + private fun setupDatePicker() { + val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, month, dayOfMonth -> + calendar.set(Calendar.YEAR, year) + calendar.set(Calendar.MONTH, month) + calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth) + updateDateInView() + } + + edtTransactionDate.setOnClickListener { + showDatePicker(dateSetListener) + } + + datePickerIcon.setOnClickListener { + showDatePicker(dateSetListener) + } + } + + private fun showDatePicker(dateSetListener: DatePickerDialog.OnDateSetListener) { + DatePickerDialog( + this, + dateSetListener, + calendar.get(Calendar.YEAR), + calendar.get(Calendar.MONTH), + calendar.get(Calendar.DAY_OF_MONTH) + ).show() + } + + private fun updateDateInView() { + val format = "yyyy-MM-dd" + val sdf = SimpleDateFormat(format, Locale.US) + edtTransactionDate.setText(sdf.format(calendar.time)) + } + + private fun fetchPaymentMethods() { + lifecycleScope.launch { + try { + val response = ApiConfig.getApiService(sessionManager).getStoreData() + if (response.isSuccessful && response.body() != null) { + val storeData = response.body()!! + paymentMethods = storeData.payment + + setupPaymentMethodSpinner() + } else { + Toast.makeText( + this@BalanceTopUpActivity, + "Gagal memuat metode pembayaran", + Toast.LENGTH_SHORT + ).show() + } + } catch (e: Exception) { + Toast.makeText( + this@BalanceTopUpActivity, + "Error: ${e.message}", + Toast.LENGTH_SHORT + ).show() + } + } + } + + private fun setupPaymentMethodSpinner() { + if (paymentMethods.isEmpty()) { + Toast.makeText( + this, + "Tidak ada metode pembayaran tersedia", + Toast.LENGTH_SHORT + ).show() + return + } + + // Debug payment methods + for (payment in paymentMethods) { + android.util.Log.d("BalanceTopUp", "Payment Option - ID: ${payment.id}, Bank: ${payment.bankName}, Number: ${payment.bankNum}") + } + + val paymentOptions = paymentMethods.map { "${it.bankName} - ${it.bankNum}" }.toTypedArray() + val adapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, paymentOptions) + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + + spinnerPaymentMethod.adapter = adapter + spinnerPaymentMethod.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { + selectedPaymentId = paymentMethods[position].id + android.util.Log.d("BalanceTopUp", "Selected payment ID: $selectedPaymentId") + } + + override fun onNothingSelected(parent: AdapterView<*>?) { + selectedPaymentId = -1 + } + } + } + + private fun submitForm() { + // Prevent multiple clicks + if (!btnSend.isEnabled) { + return + } + + // Validate inputs + if (selectedImageUri == null) { + Toast.makeText(this, "Mohon pilih foto bukti pembayaran", Toast.LENGTH_SHORT).show() + return + } + + val nominal = edtNominal.text.toString().trim() + if (nominal.isEmpty()) { + Toast.makeText(this, "Mohon isi nominal top up", Toast.LENGTH_SHORT).show() + return + } + + try { + // Validate the amount is a valid number + val amountValue = nominal.replace("[^0-9]".toRegex(), "").toLong() + if (amountValue <= 0) { + Toast.makeText(this, "Nominal harus lebih dari 0", Toast.LENGTH_SHORT).show() + return + } + } catch (e: NumberFormatException) { + Toast.makeText(this, "Format nominal tidak valid", Toast.LENGTH_SHORT).show() + return + } + + if (selectedPaymentId == -1) { + Toast.makeText(this, "Mohon pilih metode pembayaran", Toast.LENGTH_SHORT).show() + return + } + + val transactionDate = edtTransactionDate.text.toString().trim() + if (transactionDate.isEmpty()) { + Toast.makeText(this, "Mohon pilih tanggal transaksi", Toast.LENGTH_SHORT).show() + return + } + + // Show progress indicator + btnSend.text = "Mengirim..." + btnSend.isEnabled = false + + // Proceed with the API call + uploadTopUpData(nominal, selectedPaymentId.toString(), transactionDate) + } + + private fun uploadTopUpData(amount: String, paymentInfoId: String, transactionDate: String) { + lifecycleScope.launch { + try { + // Log the values being sent + android.util.Log.d("BalanceTopUp", "Amount: $amount") + android.util.Log.d("BalanceTopUp", "Payment ID: $paymentInfoId") + android.util.Log.d("BalanceTopUp", "Transaction Date: $transactionDate") + + // Find the selected payment method to get bank name + val selectedPayment = paymentMethods.find { it.id.toString() == paymentInfoId } + if (selectedPayment == null) { + Toast.makeText( + this@BalanceTopUpActivity, + "Metode pembayaran tidak valid", + Toast.LENGTH_SHORT + ).show() + return@launch + } + + val bankName = selectedPayment.bankName + val bankNum = selectedPayment.bankNum + android.util.Log.d("BalanceTopUp", "Bank Name: $bankName") + android.util.Log.d("BalanceTopUp", "Bank Number: $bankNum") + + // Get the actual file from URI + val file = uriToFile(selectedImageUri!!) + android.util.Log.d("BalanceTopUp", "File size: ${file.length()} bytes") + android.util.Log.d("BalanceTopUp", "File name: ${file.name}") + + // Create multipart file with specific JPEG content type + val requestFile = file.asRequestBody("image/jpeg".toMediaTypeOrNull()) + val imagePart = MultipartBody.Part.createFormData("topupimg", file.name, requestFile) + + // Create other request bodies - ensure proper formatting + // Make sure amount has no commas, spaces or currency symbols + val cleanedAmount = amount.replace("[^0-9]".toRegex(), "") + val amountBody = cleanedAmount.toRequestBody("text/plain".toMediaTypeOrNull()) + val paymentInfoIdBody = paymentInfoId.toRequestBody("text/plain".toMediaTypeOrNull()) + val transactionDateBody = transactionDate.toRequestBody("text/plain".toMediaTypeOrNull()) + val bankNameBody = bankName.toRequestBody("text/plain".toMediaTypeOrNull()) + val bankNumBody = bankNum.toRequestBody("text/plain".toMediaTypeOrNull()) + + // Make the API call + val response = ApiConfig.getApiService(sessionManager).addBalanceTopUp( + imagePart, + amountBody, + paymentInfoIdBody, + transactionDateBody, + bankNameBody, + bankNumBody + ) + + if (response.isSuccessful) { + // Log the complete response + val responseBody = response.body() + android.util.Log.d("BalanceTopUp", "Success response: ${responseBody?.message}") + + // Show the actual message from backend + val successMessage = "Top Up Berhasil" + Toast.makeText( + this@BalanceTopUpActivity, + successMessage, + Toast.LENGTH_LONG + ).show() + + // Show a dialog with the success message + runOnUiThread { + androidx.appcompat.app.AlertDialog.Builder(this@BalanceTopUpActivity) + .setTitle("Berhasil") + .setMessage(successMessage) + .setPositiveButton("OK") { dialog, _ -> + dialog.dismiss() + finish() + } + .show() + } + } else { + // Get more detailed error information + val errorBody = response.errorBody()?.string() + android.util.Log.e("BalanceTopUp", "Error body: $errorBody") + android.util.Log.e("BalanceTopUp", "Error code: ${response.code()}") + + // Try to parse the error body to extract the message + var errorMessage = "Gagal mengirim permintaan: ${response.message() ?: "Error ${response.code()}"}" + try { + val jsonObject = org.json.JSONObject(errorBody ?: "{}") + if (jsonObject.has("message")) { + errorMessage = jsonObject.getString("message") + } + } catch (e: Exception) { + android.util.Log.e("BalanceTopUp", "Error parsing error body", e) + } + + Toast.makeText( + this@BalanceTopUpActivity, + errorMessage, + Toast.LENGTH_LONG + ).show() + + // Show a dialog with the error message + runOnUiThread { + androidx.appcompat.app.AlertDialog.Builder(this@BalanceTopUpActivity) + .setTitle("Error Response") + .setMessage(errorMessage) + .setPositiveButton("OK") { dialog, _ -> + dialog.dismiss() + } + .show() + } + } + } catch (e: Exception) { + android.util.Log.e("BalanceTopUp", "Exception: ${e.message}", e) + Toast.makeText( + this@BalanceTopUpActivity, + "Error: ${e.message}", + Toast.LENGTH_SHORT + ).show() + } finally { + // Reset button state + btnSend.text = "Kirim" + btnSend.isEnabled = true + } + } + } + + private fun uriToFile(uri: Uri): File { + val inputStream = contentResolver.openInputStream(uri) + val tempFile = File.createTempFile("upload", ".jpg", cacheDir) + tempFile.deleteOnExit() + + inputStream?.use { input -> + tempFile.outputStream().use { output -> + input.copyTo(output) + } + } + + // Validate file isn't empty + if (tempFile.length() == 0L) { + throw IllegalStateException("File is empty") + } + + return tempFile + } } \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/DetailStoreProfileActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/DetailStoreProfileActivity.kt index 8e098d3..3cfb95f 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/DetailStoreProfileActivity.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/DetailStoreProfileActivity.kt @@ -1,15 +1,20 @@ package com.alya.ecommerce_serang.ui.profile.mystore.profile +import android.app.Activity +import android.content.Intent 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 com.alya.ecommerce_serang.R import com.alya.ecommerce_serang.data.api.dto.Store import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig import com.alya.ecommerce_serang.data.api.retrofit.ApiService import com.alya.ecommerce_serang.data.repository.MyStoreRepository import com.alya.ecommerce_serang.databinding.ActivityDetailStoreProfileBinding +import com.alya.ecommerce_serang.ui.profile.mystore.profile.address.DetailStoreAddressActivity import com.alya.ecommerce_serang.utils.viewmodel.MyStoreViewModel import com.alya.ecommerce_serang.utils.BaseViewModelFactory import com.alya.ecommerce_serang.utils.SessionManager @@ -39,6 +44,24 @@ class DetailStoreProfileActivity : AppCompatActivity() { enableEdgeToEdge() + // Set up header title + binding.header.headerTitle.text = "Profil Toko" + + // Set up back button + binding.header.headerLeftIcon.setOnClickListener { + onBackPressedDispatcher.onBackPressed() + } + + binding.btnEditStoreProfile.setOnClickListener { + val intent = Intent(this, EditStoreProfileActivity::class.java) + startActivityForResult(intent, EDIT_PROFILE_REQUEST_CODE) + } + + binding.layoutAddress.setOnClickListener { + val intent = Intent(this, DetailStoreAddressActivity::class.java) + startActivity(intent) + } + viewModel.loadMyStore() viewModel.myStoreProfile.observe(this){ user -> @@ -50,11 +73,48 @@ class DetailStoreProfileActivity : AppCompatActivity() { } } - private fun updateStoreProfile(store: Store){ + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (requestCode == EDIT_PROFILE_REQUEST_CODE && resultCode == Activity.RESULT_OK) { + // Refresh the profile data + Toast.makeText(this, "Profil toko berhasil diperbarui", Toast.LENGTH_SHORT).show() + viewModel.loadMyStore() + // Pass the result back to parent activity + setResult(Activity.RESULT_OK) + } else if (requestCode == ADDRESS_REQUEST_CODE && resultCode == Activity.RESULT_OK) { + // Refresh the profile data after address update + Toast.makeText(this, "Alamat toko berhasil diperbarui", Toast.LENGTH_SHORT).show() + viewModel.loadMyStore() + + // Pass the result back to parent activity + setResult(Activity.RESULT_OK) + } + } + + companion object { + private const val EDIT_PROFILE_REQUEST_CODE = 100 + private const val ADDRESS_REQUEST_CODE = 101 + } + + private fun updateStoreProfile(store: Store){ + // Update text fields binding.edtNamaToko.setText(store.storeName.toString()) binding.edtJenisToko.setText(store.storeType.toString()) binding.edtDeskripsiToko.setText(store.storeDescription.toString()) + // Update store image if available + if (store.storeImage != null && store.storeImage.toString().isNotEmpty() && store.storeImage.toString() != "null") { + val imageUrl = "http:/192.168.100.156:3000${store.storeImage}" + Log.d("DetailStoreProfile", "Loading image from: $imageUrl") + + Glide.with(this) + .load(imageUrl) + .placeholder(R.drawable.placeholder_image) + .error(R.drawable.placeholder_image) + .into(binding.ivProfile) + } else { + Log.d("DetailStoreProfile", "No store image available") + } } } \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/EditStoreProfileActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/EditStoreProfileActivity.kt new file mode 100644 index 0000000..2d31f3b --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/EditStoreProfileActivity.kt @@ -0,0 +1,304 @@ +package com.alya.ecommerce_serang.ui.profile.mystore.profile + +import android.app.Activity +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.provider.MediaStore +import android.util.Log +import android.view.View +import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.lifecycleScope +import com.alya.ecommerce_serang.R +import com.alya.ecommerce_serang.data.api.dto.Store +import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig +import com.alya.ecommerce_serang.databinding.ActivityEditStoreProfileBinding +import com.alya.ecommerce_serang.utils.SessionManager +import com.bumptech.glide.Glide +import kotlinx.coroutines.launch +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody.Companion.asRequestBody +import okhttp3.RequestBody.Companion.toRequestBody +import java.io.File +import java.io.FileOutputStream + +class EditStoreProfileActivity : AppCompatActivity() { + private lateinit var binding: ActivityEditStoreProfileBinding + private lateinit var sessionManager: SessionManager + private var storeImageUri: Uri? = null + private lateinit var currentStore: Store + + private val pickImage = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == Activity.RESULT_OK) { + result.data?.data?.let { uri -> + storeImageUri = uri + Log.d("EditStoreProfile", "Image selected: $uri") + + // Set the image to the ImageView for immediate preview + try { + binding.ivStoreImage.setImageURI(null) // Clear any previous image + binding.ivStoreImage.setImageURI(uri) + + // Alternative way using Glide for more reliable preview + Glide.with(this) + .load(uri) + .placeholder(R.drawable.placeholder_image) + .error(R.drawable.placeholder_image) + .into(binding.ivStoreImage) + + Toast.makeText(this, "Gambar berhasil dipilih", Toast.LENGTH_SHORT).show() + } catch (e: Exception) { + Log.e("EditStoreProfile", "Error displaying image preview", e) + Toast.makeText(this, "Gagal menampilkan preview gambar", Toast.LENGTH_SHORT).show() + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityEditStoreProfileBinding.inflate(layoutInflater) + setContentView(binding.root) + + sessionManager = SessionManager(this) + + // Set up header + binding.header.headerTitle.text = "Edit Profil Toko" + binding.header.headerLeftIcon.setOnClickListener { finish() } + + loadStoreData() + setupClickListeners() + } + + private fun loadStoreData() { + binding.progressBar.visibility = View.VISIBLE + lifecycleScope.launch { + try { + val response = ApiConfig.getApiService(sessionManager).getStore() + binding.progressBar.visibility = View.GONE + + if (response.isSuccessful && response.body() != null) { + currentStore = response.body()!!.store + populateFields(currentStore) + } else { + showError("Gagal memuat profil toko") + } + } catch (e: Exception) { + binding.progressBar.visibility = View.GONE + showError("Terjadi kesalahan: ${e.message}") + } + } + } + + private fun populateFields(store: Store) { + // Load store image + if (store.storeImage != null && store.storeImage.toString().isNotEmpty() && store.storeImage.toString() != "null") { + Glide.with(this) + .load(store.storeImage.toString()) + .placeholder(R.drawable.placeholder_image) + .error(R.drawable.placeholder_image) + .into(binding.ivStoreImage) + } + + // Set other fields + binding.edtStoreName.setText(store.storeName) + binding.edtDescription.setText(store.storeDescription) + binding.edtUserPhone.setText(store.userPhone) + + // Set is on leave + binding.switchIsOnLeave.isChecked = store.isOnLeave + } + + private fun setupClickListeners() { + binding.btnSelectStoreImage.setOnClickListener { + val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI) + pickImage.launch(intent) + } + + binding.btnSave.setOnClickListener { + saveStoreProfile() + } + } + + private fun saveStoreProfile() { + val storeName = binding.edtStoreName.text.toString() + val storeDescription = binding.edtDescription.text.toString() + val userPhone = binding.edtUserPhone.text.toString() + val storeStatus = currentStore.storeStatus // Keep the current status + val isOnLeave = binding.switchIsOnLeave.isChecked + + if (storeName.isEmpty() || userPhone.isEmpty()) { + showError("Nama toko dan nomor telepon harus diisi") + return + } + + binding.progressBar.visibility = View.VISIBLE + binding.btnSave.isEnabled = false + + // Show progress indicator on the image if we're uploading one + if (storeImageUri != null) { + binding.progressImage.visibility = View.VISIBLE + } + + lifecycleScope.launch { + try { + Log.d("EditStoreProfile", "Starting profile update process") + + // Create multipart request for image if selected + var storeImagePart: MultipartBody.Part? = null + if (storeImageUri != null) { + try { + val storeImageFile = uriToFile(storeImageUri!!) + Log.d("EditStoreProfile", "Image file created: ${storeImageFile.name}, size: ${storeImageFile.length()}") + + // Get the MIME type + val mimeType = contentResolver.getType(storeImageUri!!) ?: "image/jpeg" + Log.d("EditStoreProfile", "MIME type: $mimeType") + + val storeImageRequestBody = storeImageFile.asRequestBody(mimeType.toMediaTypeOrNull()) + storeImagePart = MultipartBody.Part.createFormData("storeimg", storeImageFile.name, storeImageRequestBody) + Log.d("EditStoreProfile", "Image part created with name: storeimg, filename: ${storeImageFile.name}") + } catch (e: Exception) { + Log.e("EditStoreProfile", "Error creating image part", e) + runOnUiThread { + Toast.makeText(this@EditStoreProfileActivity, "Error preparing image: ${e.message}", Toast.LENGTH_SHORT).show() + } + } + } + + // Create text parts + val nameRequestBody = storeName.toRequestBody("text/plain".toMediaTypeOrNull()) + val descriptionRequestBody = storeDescription.toRequestBody("text/plain".toMediaTypeOrNull()) + val userPhoneRequestBody = userPhone.toRequestBody("text/plain".toMediaTypeOrNull()) + val statusRequestBody = storeStatus.toRequestBody("text/plain".toMediaTypeOrNull()) + val onLeaveRequestBody = isOnLeave.toString().toRequestBody("text/plain".toMediaTypeOrNull()) + + // Log request parameters + Log.d("EditStoreProfile", "Request parameters: " + + "\nstore_name: $storeName" + + "\nstore_status: $storeStatus" + + "\nstore_description: $storeDescription" + + "\nis_on_leave: $isOnLeave" + + "\nuser_phone: $userPhone" + + "\nimage: ${storeImageUri != null}") + + // Log all parts for debugging + Log.d("EditStoreProfile", "Request parts:" + + "\nstoreName: $nameRequestBody" + + "\nstoreStatus: $statusRequestBody" + + "\nstoreDescription: $descriptionRequestBody" + + "\nisOnLeave: $onLeaveRequestBody" + + "\nuserPhone: $userPhoneRequestBody" + + "\nstoreimg: ${storeImagePart != null}") + + val response = ApiConfig.getApiService(sessionManager).updateStoreProfileMultipart( + storeName = nameRequestBody, + storeStatus = statusRequestBody, + storeDescription = descriptionRequestBody, + isOnLeave = onLeaveRequestBody, + cityId = currentStore.cityId.toString().toRequestBody("text/plain".toMediaTypeOrNull()), + provinceId = currentStore.provinceId.toString().toRequestBody("text/plain".toMediaTypeOrNull()), + street = currentStore.street.toRequestBody("text/plain".toMediaTypeOrNull()), + subdistrict = currentStore.subdistrict.toRequestBody("text/plain".toMediaTypeOrNull()), + detail = currentStore.detail.toRequestBody("text/plain".toMediaTypeOrNull()), + postalCode = currentStore.postalCode.toRequestBody("text/plain".toMediaTypeOrNull()), + latitude = currentStore.latitude.toRequestBody("text/plain".toMediaTypeOrNull()), + longitude = currentStore.longitude.toRequestBody("text/plain".toMediaTypeOrNull()), + userPhone = userPhoneRequestBody, + storeimg = storeImagePart + ) + + Log.d("EditStoreProfile", "Response received: isSuccessful=${response.isSuccessful}, code=${response.code()}") + + runOnUiThread { + binding.progressBar.visibility = View.GONE + binding.progressImage.visibility = View.GONE + binding.btnSave.isEnabled = true + + if (response.isSuccessful) { + Log.d("EditStoreProfile", "Response body: ${response.body()?.toString()}") + // Try to log the updated store image URL + response.body()?.let { responseBody -> + val updatedStoreImage = responseBody.store?.storeImage + Log.d("EditStoreProfile", "Updated store image URL: $updatedStoreImage") + } + showSuccess("Profil toko berhasil diperbarui") + setResult(Activity.RESULT_OK) + finish() + } else { + val errorBodyString = response.errorBody()?.string() ?: "Error body is null" + Log.e("EditStoreProfile", "Full error response: $errorBodyString") + Log.e("EditStoreProfile", "Response headers: ${response.headers()}") + showError("Gagal memperbarui profil toko (${response.code()})") + } + } + } catch (e: Exception) { + Log.e("EditStoreProfile", "Exception during API call", e) + + runOnUiThread { + binding.progressBar.visibility = View.GONE + binding.progressImage.visibility = View.GONE + binding.btnSave.isEnabled = true + showError("Error: ${e.message}") + } + } + } + } + + private fun uriToFile(uri: Uri): File { + val contentResolver = applicationContext.contentResolver + val fileExtension = getFileExtension(contentResolver, uri) + val timeStamp = System.currentTimeMillis() + val fileName = "IMG_${timeStamp}.$fileExtension" + val tempFile = File(cacheDir, fileName) + + Log.d("EditStoreProfile", "Creating temp file: ${tempFile.absolutePath}") + + try { + contentResolver.openInputStream(uri)?.use { inputStream -> + FileOutputStream(tempFile).use { outputStream -> + val buffer = ByteArray(4 * 1024) // 4k buffer + var bytesRead: Int + while (inputStream.read(buffer).also { bytesRead = it } != -1) { + outputStream.write(buffer, 0, bytesRead) + } + outputStream.flush() + } + } + + Log.d("EditStoreProfile", "File created successfully: ${tempFile.name}, size: ${tempFile.length()} bytes") + return tempFile + } catch (e: Exception) { + Log.e("EditStoreProfile", "Error creating file from URI", e) + throw e + } + } + + private fun getFileExtension(contentResolver: android.content.ContentResolver, uri: Uri): String { + val mimeType = contentResolver.getType(uri) + return if (mimeType != null) { + val mime = android.webkit.MimeTypeMap.getSingleton() + mime.getExtensionFromMimeType(mimeType) ?: "jpg" + } else { + // If mime type is null, try to get from URI path + val path = uri.path + if (path != null) { + val extension = android.webkit.MimeTypeMap.getFileExtensionFromUrl(path) + if (!extension.isNullOrEmpty()) { + extension + } else "jpg" + } else "jpg" + } + } + + private fun showSuccess(message: String) { + Toast.makeText(this, message, Toast.LENGTH_LONG).show() + } + + private fun showError(message: String) { + Toast.makeText(this, message, Toast.LENGTH_LONG).show() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/address/DetailStoreAddressActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/address/DetailStoreAddressActivity.kt index 94f258c..897e6c8 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/address/DetailStoreAddressActivity.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/address/DetailStoreAddressActivity.kt @@ -1,21 +1,312 @@ package com.alya.ecommerce_serang.ui.profile.mystore.profile.address +import android.app.Activity +import android.app.AlertDialog import android.os.Bundle -import androidx.activity.enableEdgeToEdge +import android.util.Log +import android.view.View +import android.widget.AdapterView +import android.widget.ArrayAdapter +import android.widget.Toast +import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat -import com.alya.ecommerce_serang.R +import com.alya.ecommerce_serang.BuildConfig +import com.alya.ecommerce_serang.data.api.dto.City +import com.alya.ecommerce_serang.data.api.dto.Province +import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig +import com.alya.ecommerce_serang.data.api.retrofit.ApiService +import com.alya.ecommerce_serang.data.repository.AddressRepository +import com.alya.ecommerce_serang.databinding.ActivityDetailStoreAddressBinding +import com.alya.ecommerce_serang.utils.viewmodel.AddressViewModel +import com.alya.ecommerce_serang.utils.BaseViewModelFactory +import com.alya.ecommerce_serang.utils.SessionManager +import com.google.android.material.snackbar.Snackbar class DetailStoreAddressActivity : AppCompatActivity() { + private lateinit var binding: ActivityDetailStoreAddressBinding + private lateinit var apiService: ApiService + private lateinit var sessionManager: SessionManager + + private var selectedProvinceId: String? = null + private var provinces: List = emptyList() + private var cities: List = emptyList() + + private val TAG = "StoreAddressActivity" + + private val viewModel: AddressViewModel by viewModels { + BaseViewModelFactory { + val apiService = ApiConfig.getApiService(sessionManager) + val addressRepository = AddressRepository(apiService) + AddressViewModel(addressRepository) + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - enableEdgeToEdge() - setContentView(R.layout.activity_detail_store_address) - 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 + binding = ActivityDetailStoreAddressBinding.inflate(layoutInflater) + setContentView(binding.root) + + sessionManager = SessionManager(this) + apiService = ApiConfig.getApiService(sessionManager) + + // Log the base URL + Log.d(TAG, "BASE_URL: ${BuildConfig.BASE_URL}") + + // Add error text view + binding.tvError.visibility = View.GONE + + // Set up header title + binding.header.headerTitle.text = "Alamat Toko" + + // Set up back button + binding.header.headerLeftIcon.setOnClickListener { + onBackPressedDispatcher.onBackPressed() + } + + setupSpinners() + setupObservers() + setupSaveButton() + + // Add retry button + binding.btnRetry.setOnClickListener { + binding.tvError.visibility = View.GONE + binding.progressBar.visibility = View.VISIBLE + Log.d(TAG, "Retrying to fetch provinces...") + viewModel.fetchProvinces() + } + + // Show loading spinners initially + showProvinceLoading(true) + + // Load existing address data first + Log.d(TAG, "Fetching store address...") + viewModel.fetchStoreAddress() + + // Load provinces data + Log.d(TAG, "Fetching provinces...") + viewModel.fetchProvinces() + } + + private fun setupSpinners() { + // Province spinner listener + binding.spinnerProvince.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { + Log.d(TAG, "Province selected at position: $position") + if (position > 0 && provinces.isNotEmpty()) { + selectedProvinceId = provinces[position - 1].provinceId + Log.d(TAG, "Selected province ID: $selectedProvinceId") + selectedProvinceId?.let { + Log.d(TAG, "Fetching cities for province ID: $it") + showCityLoading(true) + viewModel.fetchCities(it) + } + } + } + + override fun onNothingSelected(p0: AdapterView<*>?) { + // Do nothing + } + } + } + + private fun setupObservers() { + // Observe provinces data + viewModel.provinces.observe(this) { provinceList -> + Log.d(TAG, "Received provinces: ${provinceList.size}") + showProvinceLoading(false) + + if (provinceList.isEmpty()) { + showError("No provinces available") + return@observe + } + + provinces = provinceList + val provinceNames = mutableListOf("Pilih Provinsi") + provinceNames.addAll(provinceList.map { it.provinceName }) + + Log.d(TAG, "Province names: $provinceNames") + + val adapter = ArrayAdapter( + this, + android.R.layout.simple_spinner_item, + provinceNames + ) + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + binding.spinnerProvince.adapter = adapter + } + + // Observe cities data + viewModel.cities.observe(this) { cityList -> + Log.d(TAG, "Received cities: ${cityList.size}") + showCityLoading(false) + + cities = cityList + val cityNames = mutableListOf("Pilih Kota/Kabupaten") + cityNames.addAll(cityList.map { it.cityName }) + + Log.d(TAG, "City names: $cityNames") + + val adapter = ArrayAdapter( + this, + android.R.layout.simple_spinner_item, + cityNames + ) + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + binding.spinnerCity.adapter = adapter + + // If we have a stored city_id, select it + viewModel.storeAddress.value?.let { address -> + if (address.cityId.isNotEmpty()) { + val cityIndex = cities.indexOfFirst { city -> + city.cityId == address.cityId + } + Log.d(TAG, "City index for ID ${address.cityId}: $cityIndex") + if (cityIndex != -1) { + binding.spinnerCity.setSelection(cityIndex + 1) // +1 because of "Pilih Kota/Kabupaten" + } + } + } + } + + // Observe store address data + viewModel.storeAddress.observe(this) { address -> + Log.d(TAG, "Received store address: $address") + address?.let { + // Set the fields + binding.edtStreet.setText(address.street) + binding.edtSubdistrict.setText(address.subdistrict) + binding.edtDetailAddress.setText(address.detail ?: "") + binding.edtPostalCode.setText(address.postalCode) + + // Handle latitude and longitude + val lat = if (address.latitude == null || address.latitude.toString() == "NaN") 0.0 else address.latitude + val lng = if (address.longitude == null || address.longitude.toString() == "NaN") 0.0 else address.longitude + + // Set selected province ID to trigger city loading + if (address.provinceId.isNotEmpty()) { + selectedProvinceId = address.provinceId + + // Find province index and select it after provinces are loaded + if (provinces.isNotEmpty()) { + val provinceIndex = provinces.indexOfFirst { province -> + province.provinceId == address.provinceId + } + Log.d(TAG, "Province index for ID ${address.provinceId}: $provinceIndex") + if (provinceIndex != -1) { + binding.spinnerProvince.setSelection(provinceIndex + 1) // +1 because of "Pilih Provinsi" + + // Now fetch cities for this province + showCityLoading(true) + viewModel.fetchCities(address.provinceId) + } + } + } + } + } + + // Observe loading state + viewModel.isLoading.observe(this) { isLoading -> + binding.progressBar.visibility = if (isLoading) View.VISIBLE else View.GONE + } + + // Observe error messages + viewModel.errorMessage.observe(this) { errorMsg -> + Log.e(TAG, "Error: $errorMsg") + showError(errorMsg) + } + + // Observe save success + viewModel.saveSuccess.observe(this) { success -> + if (success) { + Toast.makeText(this, "Alamat berhasil disimpan", Toast.LENGTH_SHORT).show() + setResult(Activity.RESULT_OK) + finish() + } + } + } + + private fun showProvinceLoading(isLoading: Boolean) { + binding.provinceProgressBar.visibility = if (isLoading) View.VISIBLE else View.GONE + binding.spinnerProvince.visibility = if (isLoading) View.GONE else View.VISIBLE + } + + private fun showCityLoading(isLoading: Boolean) { + binding.cityProgressBar.visibility = if (isLoading) View.VISIBLE else View.GONE + binding.spinnerCity.visibility = if (isLoading) View.GONE else View.VISIBLE + } + + private fun showError(message: String) { + binding.progressBar.visibility = View.GONE + binding.tvError.visibility = View.VISIBLE + binding.tvError.text = "Error: $message\nURL: ${BuildConfig.BASE_URL}/provinces" + binding.btnRetry.visibility = View.VISIBLE + + // Also show in a dialog for immediate attention + AlertDialog.Builder(this) + .setTitle("Error") + .setMessage("$message\n\nAPI URL: ${BuildConfig.BASE_URL}/provinces") + .setPositiveButton("Retry") { _, _ -> + binding.tvError.visibility = View.GONE + binding.progressBar.visibility = View.VISIBLE + viewModel.fetchProvinces() + } + .setNegativeButton("Cancel") { dialog, _ -> + dialog.dismiss() + } + .show() + + // Also show a snackbar + Snackbar.make(binding.root, "Error: $message", Snackbar.LENGTH_LONG) + .setAction("Retry") { + binding.tvError.visibility = View.GONE + binding.progressBar.visibility = View.VISIBLE + viewModel.fetchProvinces() + } + .show() + } + + private fun setupSaveButton() { + binding.btnSaveAddress.setOnClickListener { + val street = binding.edtStreet.text.toString() + val subdistrict = binding.edtSubdistrict.text.toString() + val detail = binding.edtDetailAddress.text.toString() + val postalCode = binding.edtPostalCode.text.toString() + val latitudeStr = TODO() + val longitudeStr = TODO() + + // Validate required fields + if (selectedProvinceId == null || binding.spinnerCity.selectedItemPosition <= 0 || + street.isEmpty() || subdistrict.isEmpty() || postalCode.isEmpty()) { + Toast.makeText(this, "Mohon lengkapi data yang wajib diisi", Toast.LENGTH_SHORT).show() + return@setOnClickListener + } + + // Get selected city + val cityPosition = binding.spinnerCity.selectedItemPosition + if (cityPosition <= 0 || cities.isEmpty() || cityPosition > cities.size) { + Toast.makeText(this, "Mohon pilih kota/kabupaten", Toast.LENGTH_SHORT).show() + return@setOnClickListener + } + + val selectedCity = cities[cityPosition - 1] + + // Parse coordinates + val latitude = latitudeStr.toDoubleOrNull() ?: 0.0 + val longitude = longitudeStr.toDoubleOrNull() ?: 0.0 + + // Save address + viewModel.saveStoreAddress( + provinceId = selectedProvinceId!!, + provinceName = provinces.find { it.provinceId == selectedProvinceId }?.provinceName ?: "", + cityId = selectedCity.cityId, + cityName = selectedCity.cityName, + street = street, + subdistrict = subdistrict, + detail = detail, + postalCode = postalCode, + latitude = latitude, + longitude = longitude + ) } } } \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/AddressViewModel.kt b/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/AddressViewModel.kt new file mode 100644 index 0000000..9f3d94a --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/AddressViewModel.kt @@ -0,0 +1,129 @@ +package com.alya.ecommerce_serang.utils.viewmodel + +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.City +import com.alya.ecommerce_serang.data.api.dto.Province +import com.alya.ecommerce_serang.data.api.dto.StoreAddress +import com.alya.ecommerce_serang.data.repository.AddressRepository +import kotlinx.coroutines.launch + +class AddressViewModel(private val addressRepository: AddressRepository) : ViewModel() { + + private val TAG = "AddressViewModel" + + private val _provinces = MutableLiveData>() + val provinces: LiveData> = _provinces + + private val _cities = MutableLiveData>() + val cities: LiveData> = _cities + + private val _storeAddress = MutableLiveData() + val storeAddress: LiveData = _storeAddress + + private val _isLoading = MutableLiveData() + val isLoading: LiveData = _isLoading + + private val _errorMessage = MutableLiveData() + val errorMessage: LiveData = _errorMessage + + private val _saveSuccess = MutableLiveData() + val saveSuccess: LiveData = _saveSuccess + + fun fetchProvinces() { + Log.d(TAG, "fetchProvinces() called") + _isLoading.value = true + viewModelScope.launch { + try { + Log.d(TAG, "Calling addressRepository.getProvinces()") + val response = addressRepository.getProvinces() + Log.d(TAG, "Received provinces response: ${response.size} provinces") + _provinces.value = response + _isLoading.value = false + } catch (e: Exception) { + Log.e(TAG, "Error fetching provinces", e) + _errorMessage.value = "Failed to load provinces: ${e.message}" + _isLoading.value = false + } + } + } + + fun fetchCities(provinceId: String) { + Log.d(TAG, "fetchCities() called with provinceId: $provinceId") + _isLoading.value = true + viewModelScope.launch { + try { + Log.d(TAG, "Calling addressRepository.getCities()") + val response = addressRepository.getCities(provinceId) + Log.d(TAG, "Received cities response: ${response.size} cities") + _cities.value = response + _isLoading.value = false + } catch (e: Exception) { + Log.e(TAG, "Error fetching cities", e) + _errorMessage.value = "Failed to load cities: ${e.message}" + _isLoading.value = false + } + } + } + + fun fetchStoreAddress() { + Log.d(TAG, "fetchStoreAddress() called") + _isLoading.value = true + viewModelScope.launch { + try { + Log.d(TAG, "Calling addressRepository.getStoreAddress()") + val response = addressRepository.getStoreAddress() + Log.d(TAG, "Received store address response: $response") + _storeAddress.value = response + _isLoading.value = false + } catch (e: Exception) { + Log.e(TAG, "Error fetching store address", e) + _errorMessage.value = "Failed to load store address: ${e.message}" + _isLoading.value = false + } + } + } + + fun saveStoreAddress( + provinceId: String, + provinceName: String, + cityId: String, + cityName: String, + street: String, + subdistrict: String, + detail: String, + postalCode: String, + latitude: Double, + longitude: Double + ) { + Log.d(TAG, "saveStoreAddress() called with provinceId: $provinceId, cityId: $cityId") + _isLoading.value = true + viewModelScope.launch { + try { + Log.d(TAG, "Calling addressRepository.saveStoreAddress()") + val success = addressRepository.saveStoreAddress( + provinceId = provinceId, + provinceName = provinceName, + cityId = cityId, + cityName = cityName, + street = street, + subdistrict = subdistrict, + detail = detail, + postalCode = postalCode, + latitude = latitude, + longitude = longitude + ) + Log.d(TAG, "Save store address result: $success") + _saveSuccess.value = success + _isLoading.value = false + } catch (e: Exception) { + Log.e(TAG, "Error saving store address", e) + _errorMessage.value = "Failed to save address: ${e.message}" + _isLoading.value = false + } + } + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_balance_top_up.xml b/app/src/main/res/layout/activity_balance_top_up.xml index 913dc1a..133e8f4 100644 --- a/app/src/main/res/layout/activity_balance_top_up.xml +++ b/app/src/main/res/layout/activity_balance_top_up.xml @@ -322,7 +322,8 @@