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 @@
+ style="@style/button.large.disabled.long"
+ android:layout_marginBottom="16dp"/>
diff --git a/app/src/main/res/layout/activity_detail_store_address.xml b/app/src/main/res/layout/activity_detail_store_address.xml
index 3efe392..837e0e5 100644
--- a/app/src/main/res/layout/activity_detail_store_address.xml
+++ b/app/src/main/res/layout/activity_detail_store_address.xml
@@ -26,7 +26,33 @@
android:paddingHorizontal="@dimen/horizontal_safe_area"
android:layout_marginTop="19dp">
-
+
+
+
+
+
+
+
+
+
-
+
+ android:orientation="horizontal"
+ android:background="@drawable/bg_text_field"
+ android:gravity="center_vertical"
+ android:layout_marginTop="10dp">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -103,94 +204,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:enabled="false"
+ android:layout_marginBottom="16dp"/>
+
+
diff --git a/app/src/main/res/layout/activity_detail_store_product.xml b/app/src/main/res/layout/activity_detail_store_product.xml
index 3922988..7be0362 100644
--- a/app/src/main/res/layout/activity_detail_store_product.xml
+++ b/app/src/main/res/layout/activity_detail_store_product.xml
@@ -847,7 +847,8 @@
android:id="@+id/btn_save_product"
android:text="Simpan Produk"
style="@style/button.large.disabled.long"
- android:enabled="false"/>
+ android:enabled="false"
+ android:layout_marginBottom="16dp"/>
diff --git a/app/src/main/res/layout/activity_detail_store_profile.xml b/app/src/main/res/layout/activity_detail_store_profile.xml
index bc29ee3..a32534f 100644
--- a/app/src/main/res/layout/activity_detail_store_profile.xml
+++ b/app/src/main/res/layout/activity_detail_store_profile.xml
@@ -8,7 +8,9 @@
android:orientation="vertical"
tools:context=".ui.profile.mystore.profile.DetailStoreProfileActivity">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index df89d71..5609b8a 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,5 +1,5 @@
[versions]
-agp = "8.5.2"
+agp = "8.10.0"
glide = "4.16.0"
hiltAndroid = "2.48" # Updated from 2.44 for better compatibility
hiltLifecycleViewmodel = "1.0.0-alpha03"
From bf810ddc3e646f4322b66aa02a701f422b8017fd Mon Sep 17 00:00:00 2001
From: Gracia Hotmauli <95269134+hotmauligracia@users.noreply.github.com>
Date: Tue, 13 May 2025 19:39:54 +0700
Subject: [PATCH 3/4] edit store profile & topup
---
.../data/api/dto/PaymentInfo.kt | 60 +++
.../data/api/dto/ShippingService.kt | 13 +
.../data/api/response/store/StoreResponse.kt | 18 +
.../response/store/profile/GenericResponse.kt | 11 +
.../store/profile/StoreDataResponse.kt | 11 +-
.../api/response/store/topup/TopUpResponse.kt | 87 ++++
.../data/api/retrofit/ApiService.kt | 58 ++-
.../data/repository/PaymentInfoRepository.kt | 168 ++++++++
.../repository/ShippingServiceRepository.kt | 78 ++++
.../ui/order/CheckoutActivity.kt | 4 +-
.../ui/profile/mystore/MyStoreActivity.kt | 54 ++-
.../mystore/balance/BalanceActivity.kt | 380 +++++++++++++++++-
.../mystore/balance/BalanceTopUpActivity.kt | 2 +-
.../balance/BalanceTransactionAdapter.kt | 89 +++-
.../profile/DetailStoreProfileActivity.kt | 34 +-
.../address/DetailStoreAddressActivity.kt | 11 +-
.../payment_info/PaymentInfoActivity.kt | 283 ++++++++++++-
.../payment_info/PaymentInfoAdapter.kt | 82 ++++
.../ShippingServiceActivity.kt | 111 ++++-
.../utils/UriToFileConverter.kt | 145 +++++++
.../utils/viewmodel/PaymentInfoViewModel.kt | 121 ++++++
.../viewmodel/ShippingServiceViewModel.kt | 80 ++++
app/src/main/res/layout/activity_balance.xml | 92 +++--
.../res/layout/activity_balance_top_up.xml | 42 --
app/src/main/res/layout/activity_checkout.xml | 2 +-
.../layout/activity_detail_store_address.xml | 95 ++++-
.../main/res/layout/activity_payment_info.xml | 75 +++-
.../res/layout/activity_shipping_service.xml | 124 +++++-
.../res/layout/dialog_add_payment_info.xml | 102 +++++
app/src/main/res/layout/item_payment_info.xml | 92 +++++
30 files changed, 2374 insertions(+), 150 deletions(-)
create mode 100644 app/src/main/java/com/alya/ecommerce_serang/data/api/dto/PaymentInfo.kt
create mode 100644 app/src/main/java/com/alya/ecommerce_serang/data/api/dto/ShippingService.kt
create mode 100644 app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/StoreResponse.kt
create mode 100644 app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/profile/GenericResponse.kt
create mode 100644 app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/topup/TopUpResponse.kt
create mode 100644 app/src/main/java/com/alya/ecommerce_serang/data/repository/PaymentInfoRepository.kt
create mode 100644 app/src/main/java/com/alya/ecommerce_serang/data/repository/ShippingServiceRepository.kt
create mode 100644 app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/payment_info/PaymentInfoAdapter.kt
create mode 100644 app/src/main/java/com/alya/ecommerce_serang/utils/UriToFileConverter.kt
create mode 100644 app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/PaymentInfoViewModel.kt
create mode 100644 app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/ShippingServiceViewModel.kt
create mode 100644 app/src/main/res/layout/dialog_add_payment_info.xml
create mode 100644 app/src/main/res/layout/item_payment_info.xml
diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/PaymentInfo.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/PaymentInfo.kt
new file mode 100644
index 0000000..2e5cdab
--- /dev/null
+++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/PaymentInfo.kt
@@ -0,0 +1,60 @@
+package com.alya.ecommerce_serang.data.api.dto
+
+import com.google.gson.annotations.SerializedName
+
+data class PaymentInfo(
+ @SerializedName("id")
+ val id: Int,
+
+ @SerializedName("bank_num")
+ val bankNum: String,
+
+ @SerializedName("bank_name")
+ val bankName: String,
+
+ @SerializedName("qris_image")
+ val qrisImage: String?,
+
+ @SerializedName("account_name")
+ val accountName: String?
+)
+
+data class PaymentInfoResponse(
+ @SerializedName("message")
+ val message: String,
+
+ @SerializedName("payment")
+ val payment: List
+)
+
+data class AddPaymentInfoRequest(
+ @SerializedName("bank_name")
+ val bankName: String,
+
+ @SerializedName("bank_num")
+ val bankNum: String,
+
+ @SerializedName("account_name")
+ val accountName: String
+
+ // qris will be sent as multipart form data
+)
+
+data class DeletePaymentInfoResponse(
+ @SerializedName("message")
+ val message: String,
+
+ @SerializedName("success")
+ val success: Boolean
+)
+
+data class AddPaymentInfoResponse(
+ @SerializedName("message")
+ val message: String,
+
+ @SerializedName("success")
+ val success: Boolean,
+
+ @SerializedName("payment_method")
+ val paymentInfo: PaymentInfo?
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/ShippingService.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/ShippingService.kt
new file mode 100644
index 0000000..f373219
--- /dev/null
+++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/ShippingService.kt
@@ -0,0 +1,13 @@
+package com.alya.ecommerce_serang.data.api.dto
+
+import com.google.gson.annotations.SerializedName
+
+data class ShippingService(
+ @SerializedName("courier")
+ val courier: String
+)
+
+data class ShippingServiceRequest(
+ @SerializedName("couriers")
+ val couriers: List
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/StoreResponse.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/StoreResponse.kt
new file mode 100644
index 0000000..7637538
--- /dev/null
+++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/StoreResponse.kt
@@ -0,0 +1,18 @@
+package com.alya.ecommerce_serang.data.api.response.store
+
+import com.google.gson.annotations.SerializedName
+
+data class StoreResponse(
+ val message: String,
+ val store: Store
+)
+
+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
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/profile/GenericResponse.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/profile/GenericResponse.kt
new file mode 100644
index 0000000..6a665b5
--- /dev/null
+++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/profile/GenericResponse.kt
@@ -0,0 +1,11 @@
+package com.alya.ecommerce_serang.data.api.response.store.profile
+
+import com.google.gson.annotations.SerializedName
+
+data class GenericResponse(
+ @SerializedName("message")
+ val message: String,
+
+ @SerializedName("success")
+ val success: Boolean = true
+)
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
index 711e8be..fa7ad97 100644
--- 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
@@ -4,9 +4,9 @@ import com.google.gson.annotations.SerializedName
data class StoreDataResponse(
val message: String,
- val store: Store,
- val shipping: List,
- val payment: List
+ val store: Store? = null,
+ val shipping: List? = emptyList(),
+ val payment: List = emptyList()
)
data class Store(
@@ -51,5 +51,6 @@ data class Payment(
val id: Int,
@SerializedName("bank_num") val bankNum: String,
@SerializedName("bank_name") val bankName: String,
- @SerializedName("qris_image") val qrisImage: String
-)
+ @SerializedName("qris_image") val qrisImage: String?,
+ @SerializedName("account_name") val accountName: String?
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/topup/TopUpResponse.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/topup/TopUpResponse.kt
new file mode 100644
index 0000000..c7cc1bb
--- /dev/null
+++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/topup/TopUpResponse.kt
@@ -0,0 +1,87 @@
+package com.alya.ecommerce_serang.data.api.response.store.topup
+
+import android.util.Log
+import com.google.gson.annotations.SerializedName
+import java.text.SimpleDateFormat
+import java.util.Date
+import java.util.Locale
+import java.util.TimeZone
+
+data class TopUpResponse(
+ val message: String,
+ val topup: List
+)
+
+data class TopUp(
+ val id: Int,
+ val amount: String,
+ @SerializedName("store_id") val storeId: Int,
+ val status: String,
+ @SerializedName("created_at") val createdAt: String,
+ val image: String,
+ @SerializedName("payment_info_id") val paymentInfoId: Int,
+ @SerializedName("transaction_date") val transactionDate: String,
+ @SerializedName("payment_method") val paymentMethod: String,
+ @SerializedName("account_name") val accountName: String?
+) {
+ fun getFormattedDate(): String {
+ try {
+ // Try to use transaction_date first, then fall back to created_at
+ val dateStr = if (transactionDate.isNotEmpty()) transactionDate else createdAt
+
+ // Try different formats to parse the date
+ val parsedDate = parseApiDate(dateStr) ?: return dateStr
+
+ // Format with Indonesian locale for month names
+ val outputFormat = SimpleDateFormat("dd MMM yyyy", Locale("id"))
+ return outputFormat.format(parsedDate)
+ } catch (e: Exception) {
+ Log.e("TopUp", "Error formatting date: ${e.message}")
+ return createdAt
+ }
+ }
+
+ private fun parseApiDate(dateStr: String): Date? {
+ if (dateStr.isEmpty()) return null
+
+ // List of possible date formats to try
+ val formats = listOf(
+ "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", // Standard ISO with milliseconds
+ "yyyy-MM-dd'T'HH:mm:ss'Z'", // ISO without milliseconds
+ "yyyy-MM-dd'T'HH:mm:ss.SSSZ", // ISO with timezone offset
+ "yyyy-MM-dd'T'HH:mm:ssZ", // ISO with timezone offset, no milliseconds
+ "yyyy-MM-dd", // Just the date part
+ "dd-MM-yyyy" // Alternative date format
+ )
+
+ for (format in formats) {
+ try {
+ val sdf = SimpleDateFormat(format, Locale.US)
+ sdf.timeZone = TimeZone.getTimeZone("UTC") // Assuming API dates are in UTC
+ return sdf.parse(dateStr)
+ } catch (e: Exception) {
+ // Try next format
+ continue
+ }
+ }
+
+ // If all formats fail, just try to extract the date part and parse it
+ try {
+ val datePart = dateStr.split("T").firstOrNull() ?: return null
+ val simpleDateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.US)
+ return simpleDateFormat.parse(datePart)
+ } catch (e: Exception) {
+ Log.e("TopUp", "Failed to parse date: $dateStr", e)
+ return null
+ }
+ }
+
+ fun getFormattedAmount(): String {
+ return try {
+ val amountValue = amount.toDouble()
+ String.format("+ Rp%,.0f", amountValue)
+ } catch (e: Exception) {
+ "Rp$amount"
+ }
+ }
+}
\ No newline at end of file
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 b86b350..fdd6198 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
@@ -2,6 +2,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.AddPaymentInfoResponse
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
@@ -14,6 +15,7 @@ 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.ShippingServiceRequest
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
@@ -51,8 +53,10 @@ 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.GenericResponse
import com.alya.ecommerce_serang.data.api.response.store.profile.StoreDataResponse
import com.alya.ecommerce_serang.data.api.response.store.topup.BalanceTopUpResponse
+import com.alya.ecommerce_serang.data.api.response.store.topup.TopUpResponse
import okhttp3.MultipartBody
import okhttp3.RequestBody
import retrofit2.Call
@@ -151,8 +155,15 @@ interface ApiService {
): Response
@GET("mystore")
- suspend fun getStore (): Response
+ suspend fun getStore(): Response
+
+ @GET("mystore")
suspend fun getStoreData(): Response
+
+ @GET("mystore")
+ suspend fun getMyStoreData(): Response
+
+ @GET("mystore")
suspend fun getStoreAddress(): Response
@GET("mystore/product") // Replace with actual endpoint
@@ -242,6 +253,12 @@ interface ApiService {
@Part complaintimg: MultipartBody.Part
): Response
+ @GET("store/topup")
+ suspend fun getTopUpHistory(): Response
+
+ @GET("store/topup")
+ suspend fun getFilteredTopUpHistory(@Query("date") date: String): Response
+
@Multipart
@POST("store/createtopup")
suspend fun addBalanceTopUp(
@@ -253,11 +270,6 @@ interface ApiService {
@Part("bank_num") bankNum: RequestBody
): Response
- @PUT("mystore/edit")
- suspend fun updateStoreProfile(
- @Body requestBody: okhttp3.RequestBody
- ): Response
-
@Multipart
@PUT("mystore/edit")
suspend fun updateStoreProfileMultipart(
@@ -277,6 +289,40 @@ interface ApiService {
@Part storeimg: MultipartBody.Part?
): Response
+ @Multipart
+ @POST("mystore/payment/add")
+ suspend fun addPaymentInfo(
+ @Part("bank_name") bankName: RequestBody,
+ @Part("bank_num") bankNum: RequestBody,
+ @Part("account_name") accountName: RequestBody,
+ @Part qris: MultipartBody.Part?
+ ): Response
+
+ @Multipart
+ @POST("mystore/payment/add")
+ suspend fun addPaymentInfoDirect(
+ @Part("bank_name") bankName: RequestBody,
+ @Part("bank_num") bankNum: RequestBody,
+ @Part("account_name") accountName: RequestBody,
+ @Part qris: MultipartBody.Part?
+ ): Response
+
+ @DELETE("mystore/payment/delete/{id}")
+ suspend fun deletePaymentInfo(
+ @Path("id") paymentMethodId: Int
+ ): Response
+
+ // Shipping Service API endpoints
+ @POST("mystore/shipping/add")
+ suspend fun addShippingService(
+ @Body request: ShippingServiceRequest
+ ): Response
+
+ @POST("mystore/shipping/delete")
+ suspend fun deleteShippingService(
+ @Body request: ShippingServiceRequest
+ ): Response
+
@GET("provinces")
suspend fun getProvinces(): Response
diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/repository/PaymentInfoRepository.kt b/app/src/main/java/com/alya/ecommerce_serang/data/repository/PaymentInfoRepository.kt
new file mode 100644
index 0000000..b8803d4
--- /dev/null
+++ b/app/src/main/java/com/alya/ecommerce_serang/data/repository/PaymentInfoRepository.kt
@@ -0,0 +1,168 @@
+package com.alya.ecommerce_serang.data.repository
+
+import android.util.Log
+import com.alya.ecommerce_serang.data.api.dto.PaymentInfo
+import com.alya.ecommerce_serang.data.api.retrofit.ApiService
+import com.google.gson.Gson
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import okhttp3.MediaType.Companion.toMediaTypeOrNull
+import okhttp3.MultipartBody
+import okhttp3.RequestBody.Companion.asRequestBody
+import okhttp3.RequestBody.Companion.toRequestBody
+import java.io.File
+
+class PaymentInfoRepository(private val apiService: ApiService) {
+
+ private val TAG = "PaymentInfoRepository"
+ private val gson = Gson()
+
+ suspend fun getPaymentInfo(): List = withContext(Dispatchers.IO) {
+ try {
+ Log.d(TAG, "Getting payment info")
+ val response = apiService.getStoreData()
+
+ if (response.isSuccessful) {
+ val result = response.body()
+
+ // Log the raw response
+ Log.d(TAG, "API Response body: ${gson.toJson(result)}")
+
+ // Check if payment list is null or empty
+ val paymentList = result?.payment
+ if (paymentList.isNullOrEmpty()) {
+ Log.d(TAG, "Payment list is null or empty in response")
+ return@withContext emptyList()
+ }
+
+ Log.d(TAG, "Raw payment list: ${gson.toJson(paymentList)}")
+ Log.d(TAG, "Get payment methods success: ${paymentList.size} methods")
+
+ // Convert Payment objects to PaymentMethod objects
+ val convertedPayments = paymentList.map { payment ->
+ PaymentInfo(
+ id = payment.id,
+ bankNum = payment.bankNum,
+ bankName = payment.bankName,
+ qrisImage = payment.qrisImage,
+ accountName = payment.accountName
+ )
+ }
+
+ return@withContext convertedPayments
+ } else {
+ val errorBody = response.errorBody()?.string() ?: "Unknown error"
+ Log.e(TAG, "Get payment methods error: $errorBody, HTTP code: ${response.code()}")
+ throw Exception("Failed to get payment methods: ${response.message()}, code: ${response.code()}, error: $errorBody")
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Exception getting payment methods", e)
+ throw e
+ }
+ }
+
+ suspend fun addPaymentMethod(
+ bankName: String,
+ bankNumber: String,
+ accountName: String,
+ qrisImageFile: File?
+ ): Boolean = withContext(Dispatchers.IO) {
+ try {
+ Log.d(TAG, "===== START PAYMENT METHOD ADD =====")
+ Log.d(TAG, "Adding payment method with parameters:")
+ Log.d(TAG, "Bank Name: $bankName")
+ Log.d(TAG, "Bank Number: $bankNumber")
+ Log.d(TAG, "Account Name: $accountName")
+ Log.d(TAG, "QRIS Image File: ${qrisImageFile?.absolutePath}")
+ Log.d(TAG, "QRIS File exists: ${qrisImageFile?.exists()}")
+ Log.d(TAG, "QRIS File size: ${qrisImageFile?.length() ?: 0} bytes")
+
+ // Create text RequestBody objects with explicit content type
+ val contentType = "text/plain".toMediaTypeOrNull()
+ val bankNamePart = bankName.toRequestBody(contentType)
+ val bankNumPart = bankNumber.toRequestBody(contentType)
+ val accountNamePart = accountName.toRequestBody(contentType)
+
+ // Log request parameters details
+ Log.d(TAG, "Request parameters details:")
+ Log.d(TAG, "bank_name RequestBody created with value: $bankName")
+ Log.d(TAG, "bank_num RequestBody created with value: $bankNumber")
+ Log.d(TAG, "account_name RequestBody created with value: $accountName")
+
+ // Create image part if file exists
+ var qrisPart: MultipartBody.Part? = null
+ if (qrisImageFile != null && qrisImageFile.exists() && qrisImageFile.length() > 0) {
+ // Use image/* content type to ensure proper MIME type for images
+ val imageContentType = "image/jpeg".toMediaTypeOrNull()
+ val requestFile = qrisImageFile.asRequestBody(imageContentType)
+ qrisPart = MultipartBody.Part.createFormData("qris", qrisImageFile.name, requestFile)
+ Log.d(TAG, "qris MultipartBody.Part created with filename: ${qrisImageFile.name}")
+ Log.d(TAG, "qris file size: ${qrisImageFile.length()} bytes")
+ } else {
+ Log.d(TAG, "No qris image part will be included in the request")
+ }
+
+ // Example input data being sent to API
+ Log.d(TAG, "Example input data sent to API endpoint http://192.168.100.31:3000/mystore/payment/add:")
+ Log.d(TAG, "Method: POST")
+ Log.d(TAG, "Content-Type: multipart/form-data")
+ Log.d(TAG, "Form fields:")
+ Log.d(TAG, "- bank_name: $bankName")
+ Log.d(TAG, "- bank_num: $bankNumber")
+ Log.d(TAG, "- account_name: $accountName")
+ if (qrisPart != null) {
+ Log.d(TAG, "- qris: [binary image file: ${qrisImageFile?.name}, size: ${qrisImageFile?.length()} bytes]")
+ }
+
+ try {
+ // Use the direct API method call
+ val response = apiService.addPaymentInfoDirect(
+ bankName = bankNamePart,
+ bankNum = bankNumPart,
+ accountName = accountNamePart,
+ qris = qrisPart
+ )
+
+ if (response.isSuccessful) {
+ val result = response.body()
+ Log.d(TAG, "API response: ${gson.toJson(result)}")
+ Log.d(TAG, "Add payment method success")
+ Log.d(TAG, "===== END PAYMENT METHOD ADD - SUCCESS =====")
+ return@withContext true
+ } else {
+ val errorBody = response.errorBody()?.string() ?: "Unknown error"
+ Log.e(TAG, "Add payment method error: $errorBody, HTTP code: ${response.code()}")
+ Log.e(TAG, "===== END PAYMENT METHOD ADD - FAILURE =====")
+ throw Exception("Failed to add payment method: ${response.message()}, code: ${response.code()}, error: $errorBody")
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "API call exception", e)
+ throw e
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Exception adding payment method", e)
+ Log.e(TAG, "===== END PAYMENT METHOD ADD - EXCEPTION =====")
+ throw e
+ }
+ }
+
+ suspend fun deletePaymentMethod(paymentMethodId: Int): Boolean = withContext(Dispatchers.IO) {
+ try {
+ Log.d(TAG, "Deleting payment method with ID: $paymentMethodId")
+
+ val response = apiService.deletePaymentInfo(paymentMethodId)
+
+ if (response.isSuccessful) {
+ Log.d(TAG, "Delete payment method success: ${response.body()?.message}")
+ return@withContext true
+ } else {
+ val errorBody = response.errorBody()?.string() ?: "Unknown error"
+ Log.e(TAG, "Delete payment method error: $errorBody, HTTP code: ${response.code()}")
+ throw Exception("Failed to delete payment method: ${response.message()}, code: ${response.code()}, error: $errorBody")
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Exception deleting payment method", e)
+ throw e
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/repository/ShippingServiceRepository.kt b/app/src/main/java/com/alya/ecommerce_serang/data/repository/ShippingServiceRepository.kt
new file mode 100644
index 0000000..770613b
--- /dev/null
+++ b/app/src/main/java/com/alya/ecommerce_serang/data/repository/ShippingServiceRepository.kt
@@ -0,0 +1,78 @@
+package com.alya.ecommerce_serang.data.repository
+
+import android.util.Log
+import com.alya.ecommerce_serang.data.api.dto.ShippingServiceRequest
+import com.alya.ecommerce_serang.data.api.retrofit.ApiService
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+class ShippingServiceRepository(private val apiService: ApiService) {
+
+ private val TAG = "ShippingServiceRepo"
+
+ suspend fun getAvailableCouriers(): List = withContext(Dispatchers.IO) {
+ try {
+ Log.d(TAG, "Getting available shipping services")
+ val response = apiService.getStoreData()
+
+ if (response.isSuccessful) {
+ val result = response.body()
+ val shippingList = result?.shipping
+
+ val couriers = shippingList?.map { it.courier } ?: emptyList()
+
+ Log.d(TAG, "Get shipping services success: ${couriers.size} couriers")
+ return@withContext couriers
+ } else {
+ val errorBody = response.errorBody()?.string() ?: "Unknown error"
+ Log.e(TAG, "Get shipping services error: $errorBody")
+ throw Exception("Failed to get shipping services: ${response.message()}")
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Exception getting shipping services", e)
+ throw e
+ }
+ }
+
+ suspend fun addShippingServices(couriers: List): Boolean = withContext(Dispatchers.IO) {
+ try {
+ Log.d(TAG, "Adding shipping services: $couriers")
+
+ val request = ShippingServiceRequest(couriers = couriers)
+ val response = apiService.addShippingService(request)
+
+ if (response.isSuccessful) {
+ Log.d(TAG, "Add shipping services success: ${response.body()?.message}")
+ return@withContext true
+ } else {
+ val errorBody = response.errorBody()?.string() ?: "Unknown error"
+ Log.e(TAG, "Add shipping services error: $errorBody")
+ throw Exception("Failed to add shipping services: ${response.message()}")
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Exception adding shipping services", e)
+ throw e
+ }
+ }
+
+ suspend fun deleteShippingServices(couriers: List): Boolean = withContext(Dispatchers.IO) {
+ try {
+ Log.d(TAG, "Deleting shipping services: $couriers")
+
+ val request = ShippingServiceRequest(couriers = couriers)
+ val response = apiService.deleteShippingService(request)
+
+ if (response.isSuccessful) {
+ Log.d(TAG, "Delete shipping services success: ${response.body()?.message}")
+ return@withContext true
+ } else {
+ val errorBody = response.errorBody()?.string() ?: "Unknown error"
+ Log.e(TAG, "Delete shipping services error: $errorBody")
+ throw Exception("Failed to delete shipping services: ${response.message()}")
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Exception deleting shipping services", e)
+ throw e
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/order/CheckoutActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/order/CheckoutActivity.kt
index f3f9628..7e25aec 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/ui/order/CheckoutActivity.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/order/CheckoutActivity.kt
@@ -161,7 +161,7 @@ class CheckoutActivity : AppCompatActivity() {
viewModel.setPaymentMethod(1)
}
- binding.rvPaymentMethods.apply {
+ binding.rvPaymentInfo.apply {
layoutManager = LinearLayoutManager(this@CheckoutActivity)
adapter = paymentAdapter
}
@@ -187,7 +187,7 @@ class CheckoutActivity : AppCompatActivity() {
}
}
- binding.rvPaymentMethods.apply {
+ binding.rvPaymentInfo.apply {
layoutManager = LinearLayoutManager(this@CheckoutActivity)
adapter = testAdapter
}
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 662e5a7..0b040d3 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
@@ -2,10 +2,12 @@ package com.alya.ecommerce_serang.ui.profile.mystore
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
@@ -16,6 +18,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.SellsActivity
import com.alya.ecommerce_serang.ui.profile.mystore.sells.SellsListFragment
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
import com.alya.ecommerce_serang.utils.SessionManager
@@ -64,10 +67,17 @@ class MyStoreActivity : AppCompatActivity() {
binding.tvStoreName.text = store.storeName
binding.tvStoreType.text = store.storeType
- store.storeImage.let {
+ if (store.storeImage != null && store.storeImage.toString().isNotEmpty() && store.storeImage.toString() != "null") {
+ val imageUrl = "http://192.168.100.156:3000${store.storeImage}"
+ Log.d("MyStoreActivity", "Loading store image from: $imageUrl")
+
Glide.with(this)
- .load(it)
+ .load(imageUrl)
+ .placeholder(R.drawable.placeholder_image)
+ .error(R.drawable.placeholder_image)
.into(binding.ivProfile)
+ } else {
+ Log.d("MyStoreActivity", "No store image available")
}
}
@@ -81,19 +91,26 @@ class MyStoreActivity : AppCompatActivity() {
}
binding.tvHistory.setOnClickListener {
- navigateToSellsFragment("all")
+ val intent = Intent(this, SellsActivity::class.java)
+ startActivity(intent)
}
binding.layoutPerluTagihan.setOnClickListener {
- navigateToSellsFragment("pending")
+ val intent = Intent(this, SellsActivity::class.java)
+ startActivity(intent)
+ //navigateToSellsFragment("pending")
}
binding.layoutPembayaran.setOnClickListener {
- navigateToSellsFragment("paid")
+ val intent = Intent(this, SellsActivity::class.java)
+ startActivity(intent)
+ //navigateToSellsFragment("paid")
}
binding.layoutPerluDikirim.setOnClickListener {
- navigateToSellsFragment("processed")
+ val intent = Intent(this, SellsActivity::class.java)
+ startActivity(intent)
+ //navigateToSellsFragment("processed")
}
binding.layoutProductMenu.setOnClickListener {
@@ -115,11 +132,24 @@ class MyStoreActivity : AppCompatActivity() {
}
}
- private fun navigateToSellsFragment(status: String) {
- val sellsFragment = SellsListFragment.newInstance(status)
- supportFragmentManager.beginTransaction()
- .replace(android.R.id.content, sellsFragment)
- .addToBackStack(null)
- .commit()
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ if (requestCode == PROFILE_REQUEST_CODE && resultCode == RESULT_OK) {
+ // Refresh store data
+ viewModel.loadMyStore()
+ Toast.makeText(this, "Profil toko berhasil diperbarui", Toast.LENGTH_SHORT).show()
+ }
}
+
+ companion object {
+ private const val PROFILE_REQUEST_CODE = 100
+ }
+
+// 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/balance/BalanceActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/balance/BalanceActivity.kt
index 24e9618..b0c36ea 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,16 +1,38 @@
package com.alya.ecommerce_serang.ui.profile.mystore.balance
+import android.app.DatePickerDialog
import android.content.Intent
import android.os.Bundle
+import android.util.Log
+import android.view.View
+import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
+import androidx.lifecycle.lifecycleScope
+import androidx.recyclerview.widget.LinearLayoutManager
import com.alya.ecommerce_serang.R
+import com.alya.ecommerce_serang.data.api.response.store.topup.TopUp
+import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
import com.alya.ecommerce_serang.databinding.ActivityBalanceBinding
+import com.alya.ecommerce_serang.utils.SessionManager
+import kotlinx.coroutines.launch
+import java.text.SimpleDateFormat
+import java.util.Calendar
+import java.util.Date
+import java.util.Locale
+import java.util.TimeZone
class BalanceActivity : AppCompatActivity() {
private lateinit var binding: ActivityBalanceBinding
+ private lateinit var topUpAdapter: BalanceTransactionAdapter
+ private lateinit var sessionManager: SessionManager
+ private val calendar = Calendar.getInstance()
+ private var selectedDate: String? = null
+ private var allTopUps: List = emptyList()
+
+ private val TAG = "BalanceActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -24,13 +46,369 @@ class BalanceActivity : AppCompatActivity() {
insets
}
+ // Initialize session manager
+ sessionManager = SessionManager(this)
+
+ // Setup header
+ val headerTitle = binding.header.headerTitle
+ headerTitle.text = "Saldo"
+
+ val backButton = binding.header.headerLeftIcon
+ backButton.setOnClickListener {
+ finish()
+ }
+
+ // Setup RecyclerView
+ setupRecyclerView()
+
+ // Setup DatePicker
+ setupDatePicker()
+
+ // Add clear filter button
+ setupClearFilter()
+
+ // Fetch data
+ fetchBalance()
+ fetchTopUpHistory()
+
+ // Setup listeners
setupListeners()
}
+ private fun setupRecyclerView() {
+ topUpAdapter = BalanceTransactionAdapter()
+ binding.rvBalanceTransaction.apply {
+ layoutManager = LinearLayoutManager(this@BalanceActivity)
+ adapter = topUpAdapter
+ }
+ }
+
+ 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()
+
+ // Store selected date for filtering
+ selectedDate = SimpleDateFormat("yyyy-MM-dd", Locale.US).format(calendar.time)
+
+ // Show debugging information
+ Log.d(TAG, "Selected date: $selectedDate")
+
+ // Display all top-up dates for debugging
+ allTopUps.forEach { topUp ->
+ Log.d(TAG, "Top-up ID: ${topUp.id}, transaction_date: ${topUp.transactionDate}, created_at: ${topUp.createdAt}")
+ }
+
+ // Apply filter
+ filterTopUpsByDate(selectedDate)
+
+ // Show clear filter button
+ binding.btnClearFilter.visibility = View.VISIBLE
+ }
+
+ binding.edtTglTransaksi.setOnClickListener {
+ showDatePicker(dateSetListener)
+ }
+
+ binding.imgDatePicker.setOnClickListener {
+ showDatePicker(dateSetListener)
+ }
+
+ binding.iconDatePicker.setOnClickListener {
+ showDatePicker(dateSetListener)
+ }
+ }
+
+ private fun setupClearFilter() {
+ binding.btnClearFilter.setOnClickListener {
+ // Clear date selection
+ binding.edtTglTransaksi.text = null
+ selectedDate = null
+
+ // Reset to show all topups
+ if (allTopUps.isNotEmpty()) {
+ updateTopUpList(allTopUps)
+ } else {
+ fetchTopUpHistory()
+ }
+
+ // Hide clear button
+ binding.btnClearFilter.visibility = View.GONE
+ }
+ }
+
+ 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 = "dd MMMM yyyy"
+ val sdf = SimpleDateFormat(format, Locale("id"))
+ binding.edtTglTransaksi.setText(sdf.format(calendar.time))
+ }
+
private fun setupListeners() {
binding.btnTopUp.setOnClickListener {
val intent = Intent(this, BalanceTopUpActivity::class.java)
- startActivity(intent)
+ startActivityForResult(intent, TOP_UP_REQUEST_CODE)
}
}
+
+ private fun fetchBalance() {
+ showLoading(true)
+ lifecycleScope.launch {
+ try {
+ val response = ApiConfig.getApiService(sessionManager).getMyStoreData()
+ if (response.isSuccessful && response.body() != null) {
+ val storeData = response.body()!!
+ val balance = storeData.store.balance
+
+ // Format the balance
+ try {
+ val balanceValue = balance.toDouble()
+ binding.tvBalance.text = String.format("Rp%,.0f", balanceValue)
+ } catch (e: Exception) {
+ binding.tvBalance.text = "Rp$balance"
+ }
+ } else {
+ Toast.makeText(
+ this@BalanceActivity,
+ "Gagal memuat data saldo: ${response.message()}",
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Error fetching balance", e)
+ Toast.makeText(
+ this@BalanceActivity,
+ "Error: ${e.message}",
+ Toast.LENGTH_SHORT
+ ).show()
+ } finally {
+ showLoading(false)
+ }
+ }
+ }
+
+ private fun fetchTopUpHistory() {
+ showLoading(true)
+ lifecycleScope.launch {
+ try {
+ val response = ApiConfig.getApiService(sessionManager).getTopUpHistory()
+
+ if (response.isSuccessful && response.body() != null) {
+ val topUpData = response.body()!!
+ allTopUps = topUpData.topup
+
+ // Apply date filter if selected
+ if (selectedDate != null) {
+ filterTopUpsByDate(selectedDate)
+ } else {
+ updateTopUpList(allTopUps)
+ }
+ } else {
+ Toast.makeText(
+ this@BalanceActivity,
+ "Gagal memuat riwayat isi ulang: ${response.message()}",
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Error fetching top-up history", e)
+ Toast.makeText(
+ this@BalanceActivity,
+ "Error: ${e.message}",
+ Toast.LENGTH_SHORT
+ ).show()
+ } finally {
+ showLoading(false)
+ }
+ }
+ }
+
+ private fun filterTopUpsByDate(dateStr: String?) {
+ if (dateStr == null || allTopUps.isEmpty()) {
+ return
+ }
+
+ try {
+ Log.d(TAG, "Filtering by date: $dateStr")
+
+ // Parse the selected date - set to start of day
+ val cal1 = Calendar.getInstance()
+ cal1.time = parseSelectedDate(dateStr)
+ cal1.set(Calendar.HOUR_OF_DAY, 0)
+ cal1.set(Calendar.MINUTE, 0)
+ cal1.set(Calendar.SECOND, 0)
+ cal1.set(Calendar.MILLISECOND, 0)
+
+ // Extract the date components we care about (year, month, day)
+ val selectedYear = cal1.get(Calendar.YEAR)
+ val selectedMonth = cal1.get(Calendar.MONTH)
+ val selectedDay = cal1.get(Calendar.DAY_OF_MONTH)
+
+ Log.d(TAG, "Selected date components: Year=$selectedYear, Month=$selectedMonth, Day=$selectedDay")
+
+ // Format for comparing with API dates
+ val filtered = allTopUps.filter { topUp ->
+ try {
+ // Debug logging
+ Log.d(TAG, "Examining top-up: ID=${topUp.id}")
+ Log.d(TAG, " - created_at=${topUp.createdAt}")
+ Log.d(TAG, " - transaction_date=${topUp.transactionDate}")
+
+ // Try both dates for more flexibility
+ val cal2 = Calendar.getInstance()
+ var matched = false
+
+ // Try transaction_date first
+ if (topUp.transactionDate.isNotEmpty()) {
+ val transactionDate = parseApiDate(topUp.transactionDate)
+ if (transactionDate != null) {
+ cal2.time = transactionDate
+ val transYear = cal2.get(Calendar.YEAR)
+ val transMonth = cal2.get(Calendar.MONTH)
+ val transDay = cal2.get(Calendar.DAY_OF_MONTH)
+
+ Log.d(TAG, " - Transaction date components: Year=$transYear, Month=$transMonth, Day=$transDay")
+
+ if (transYear == selectedYear &&
+ transMonth == selectedMonth &&
+ transDay == selectedDay) {
+ Log.d(TAG, " - MATCH on transaction_date")
+ matched = true
+ }
+ }
+ }
+
+ // If no match yet, try created_at
+ if (!matched && topUp.createdAt.isNotEmpty()) {
+ val createdAtDate = parseApiDate(topUp.createdAt)
+ if (createdAtDate != null) {
+ cal2.time = createdAtDate
+ val createdYear = cal2.get(Calendar.YEAR)
+ val createdMonth = cal2.get(Calendar.MONTH)
+ val createdDay = cal2.get(Calendar.DAY_OF_MONTH)
+
+ Log.d(TAG, " - Created date components: Year=$createdYear, Month=$createdMonth, Day=$createdDay")
+
+ if (createdYear == selectedYear &&
+ createdMonth == selectedMonth &&
+ createdDay == selectedDay) {
+ Log.d(TAG, " - MATCH on created_at")
+ matched = true
+ }
+ }
+ }
+
+ // Final result
+ Log.d(TAG, " - Match result: $matched")
+ matched
+ } catch (e: Exception) {
+ Log.e(TAG, "Date parsing error for top-up ${topUp.id}: ${e.message}", e)
+ false
+ }
+ }
+
+ Log.d(TAG, "Found ${filtered.size} matching records out of ${allTopUps.size}")
+ updateTopUpList(filtered)
+ } catch (e: Exception) {
+ Log.e(TAG, "Error filtering by date", e)
+ Toast.makeText(
+ this@BalanceActivity,
+ "Error filtering data: ${e.message}",
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ }
+
+ private fun parseSelectedDate(dateStr: String): Date {
+ // Parse the user-selected date
+ try {
+ val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.US)
+ return dateFormat.parse(dateStr) ?: Date()
+ } catch (e: Exception) {
+ Log.e(TAG, "Error parsing selected date: $dateStr", e)
+ return Date()
+ }
+ }
+
+ /**
+ * Parse ISO 8601 date format from API (handles multiple formats)
+ */
+ private fun parseApiDate(dateStr: String): Date? {
+ if (dateStr.isEmpty()) return null
+
+ // List of possible date formats to try
+ val formats = listOf(
+ "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", // Standard ISO with milliseconds
+ "yyyy-MM-dd'T'HH:mm:ss'Z'", // ISO without milliseconds
+ "yyyy-MM-dd'T'HH:mm:ss.SSSZ", // ISO with timezone offset
+ "yyyy-MM-dd'T'HH:mm:ssZ", // ISO with timezone offset, no milliseconds
+ "yyyy-MM-dd", // Just the date part
+ "dd-MM-yyyy" // Alternative date format
+ )
+
+ for (format in formats) {
+ try {
+ val sdf = SimpleDateFormat(format, Locale.US)
+ sdf.timeZone = TimeZone.getTimeZone("UTC") // Assuming API dates are in UTC
+ return sdf.parse(dateStr)
+ } catch (e: Exception) {
+ // Try next format
+ continue
+ }
+ }
+
+ // If all formats fail, just try to extract the date part and parse it
+ try {
+ val datePart = dateStr.split("T").firstOrNull() ?: return null
+ val simpleDateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.US)
+ return simpleDateFormat.parse(datePart)
+ } catch (e: Exception) {
+ Log.e(TAG, "Failed to parse date: $dateStr", e)
+ return null
+ }
+ }
+
+ private fun updateTopUpList(topUps: List) {
+ if (topUps.isEmpty()) {
+ binding.rvBalanceTransaction.visibility = View.GONE
+ binding.tvEmptyState.visibility = View.VISIBLE
+ } else {
+ binding.rvBalanceTransaction.visibility = View.VISIBLE
+ binding.tvEmptyState.visibility = View.GONE
+ topUpAdapter.submitList(topUps)
+ }
+ }
+
+ private fun showLoading(isLoading: Boolean) {
+ if (isLoading) {
+ binding.progressBar.visibility = View.VISIBLE
+ } else {
+ binding.progressBar.visibility = View.GONE
+ }
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ if (requestCode == TOP_UP_REQUEST_CODE && resultCode == RESULT_OK) {
+ // Refresh balance and top-up history after successful top-up
+ fetchBalance()
+ fetchTopUpHistory()
+ Toast.makeText(this, "Top up berhasil", Toast.LENGTH_SHORT).show()
+ }
+ }
+
+ companion object {
+ private const val TOP_UP_REQUEST_CODE = 101
+ }
}
\ 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 5ef8b31..fd85cbb 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
@@ -80,7 +80,7 @@ class BalanceTopUpActivity : AppCompatActivity() {
// Setup back button
val backButton = findViewById(R.id.header_left_icon)
backButton.setOnClickListener {
- finish()
+ onBackPressedDispatcher.onBackPressed()
}
// Setup photo selection
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/balance/BalanceTransactionAdapter.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/balance/BalanceTransactionAdapter.kt
index e106b1d..0b96498 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/balance/BalanceTransactionAdapter.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/balance/BalanceTransactionAdapter.kt
@@ -1,7 +1,90 @@
package com.alya.ecommerce_serang.ui.profile.mystore.balance
-/* class BalanceTransactionAdapter(private val balanceTransactionList: List) :
- RecyclerView.Adapter() {
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.core.content.ContextCompat
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
+import com.alya.ecommerce_serang.R
+import com.alya.ecommerce_serang.data.api.response.store.topup.TopUp
+import com.alya.ecommerce_serang.ui.profile.mystore.balance.BalanceTransactionAdapter.BalanceTransactionViewHolder
+class BalanceTransactionAdapter : ListAdapter(DIFF_CALLBACK) {
-}*/
\ No newline at end of file
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BalanceTransactionViewHolder {
+ val view = LayoutInflater.from(parent.context)
+ .inflate(R.layout.item_balance_transaction, parent, false)
+ return BalanceTransactionViewHolder(view)
+ }
+
+ override fun onBindViewHolder(holder: BalanceTransactionViewHolder, position: Int) {
+ holder.bind(getItem(position))
+ }
+
+ inner class BalanceTransactionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+ private val tvDate: TextView = itemView.findViewById(R.id.tv_balance_trans_date)
+ private val tvTitle: TextView = itemView.findViewById(R.id.tv_balance_trans_title)
+ private val tvDesc: TextView = itemView.findViewById(R.id.tv_balance_trans_desc)
+ private val tvAmount: TextView = itemView.findViewById(R.id.tv_balance_trans_amount)
+ private val ivIcon: ImageView = itemView.findViewById(R.id.iv_balance_trans_icon)
+ private val divider: View = itemView.findViewById(R.id.divider_balance_trans)
+
+ fun bind(topUp: TopUp) {
+ // Set date
+ tvDate.text = topUp.getFormattedDate()
+
+ // Set title
+ tvTitle.text = "Isi Ulang Saldo"
+
+ // Set description - payment details
+ val paymentMethod = topUp.paymentMethod
+ val accountName = topUp.accountName ?: ""
+ val desc = if (accountName.isNotEmpty()) {
+ "Isi ulang dari $paymentMethod $accountName"
+ } else {
+ "Isi ulang dari $paymentMethod"
+ }
+ tvDesc.text = desc
+
+ // Set amount
+ tvAmount.text = topUp.getFormattedAmount()
+
+ // Set color based on status
+ val context = itemView.context
+ val activeColor = ContextCompat.getColor(context, R.color.blue_500)
+ val pendingColor = ContextCompat.getColor(context, R.color.black_500)
+
+ when (topUp.status.lowercase()) {
+ "approved" -> {
+ tvAmount.setTextColor(activeColor)
+ ivIcon.setImageResource(R.drawable.ic_graph_arrow_increase)
+ }
+ "pending" -> {
+ tvAmount.setTextColor(pendingColor)
+ }
+ else -> {
+ tvAmount.setTextColor(activeColor)
+ }
+ }
+
+ // Show divider for all items except the last one
+ divider.visibility = if (bindingAdapterPosition == itemCount - 1) View.GONE else View.VISIBLE
+ }
+ }
+
+ companion object {
+ private val DIFF_CALLBACK = object : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: TopUp, newItem: TopUp): Boolean {
+ return oldItem.id == newItem.id
+ }
+
+ override fun areContentsTheSame(oldItem: TopUp, newItem: TopUp): Boolean {
+ return oldItem == newItem
+ }
+ }
+ }
+}
\ 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 3cfb95f..2200295 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
@@ -15,6 +15,8 @@ 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.ui.profile.mystore.profile.payment_info.PaymentInfoActivity
+import com.alya.ecommerce_serang.ui.profile.mystore.profile.shipping_service.ShippingServiceActivity
import com.alya.ecommerce_serang.utils.viewmodel.MyStoreViewModel
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
import com.alya.ecommerce_serang.utils.SessionManager
@@ -59,7 +61,19 @@ class DetailStoreProfileActivity : AppCompatActivity() {
binding.layoutAddress.setOnClickListener {
val intent = Intent(this, DetailStoreAddressActivity::class.java)
- startActivity(intent)
+ startActivityForResult(intent, ADDRESS_REQUEST_CODE)
+ }
+
+ // Set up payment method layout click listener
+ binding.layoutPaymentMethod.setOnClickListener {
+ val intent = Intent(this, PaymentInfoActivity::class.java)
+ startActivityForResult(intent, PAYMENT_INFO_REQUEST_CODE)
+ }
+
+ // Set up shipping services layout click listener
+ binding.layoutShipServices.setOnClickListener {
+ val intent = Intent(this, ShippingServiceActivity::class.java)
+ startActivityForResult(intent, SHIPPING_SERVICES_REQUEST_CODE)
}
viewModel.loadMyStore()
@@ -87,6 +101,20 @@ class DetailStoreProfileActivity : AppCompatActivity() {
Toast.makeText(this, "Alamat toko berhasil diperbarui", Toast.LENGTH_SHORT).show()
viewModel.loadMyStore()
+ // Pass the result back to parent activity
+ setResult(Activity.RESULT_OK)
+ } else if (requestCode == PAYMENT_INFO_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
+ // Refresh the profile data after payment method update
+ Toast.makeText(this, "Metode pembayaran berhasil diperbarui", Toast.LENGTH_SHORT).show()
+ viewModel.loadMyStore()
+
+ // Pass the result back to parent activity
+ setResult(Activity.RESULT_OK)
+ } else if (requestCode == SHIPPING_SERVICES_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
+ // Refresh the profile data after shipping services update
+ Toast.makeText(this, "Layanan pengiriman berhasil diperbarui", Toast.LENGTH_SHORT).show()
+ viewModel.loadMyStore()
+
// Pass the result back to parent activity
setResult(Activity.RESULT_OK)
}
@@ -95,6 +123,8 @@ class DetailStoreProfileActivity : AppCompatActivity() {
companion object {
private const val EDIT_PROFILE_REQUEST_CODE = 100
private const val ADDRESS_REQUEST_CODE = 101
+ private const val PAYMENT_INFO_REQUEST_CODE = 102
+ private const val SHIPPING_SERVICES_REQUEST_CODE = 103
}
private fun updateStoreProfile(store: Store){
@@ -105,7 +135,7 @@ class DetailStoreProfileActivity : AppCompatActivity() {
// 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}"
+ val imageUrl = "http://192.168.100.156:3000${store.storeImage}"
Log.d("DetailStoreProfile", "Loading image from: $imageUrl")
Glide.with(this)
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 897e6c8..9ad1f8c 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,7 +1,6 @@
package com.alya.ecommerce_serang.ui.profile.mystore.profile.address
import android.app.Activity
-import android.app.AlertDialog
import android.os.Bundle
import android.util.Log
import android.view.View
@@ -9,6 +8,7 @@ import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Toast
import androidx.activity.viewModels
+import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.alya.ecommerce_serang.BuildConfig
import com.alya.ecommerce_serang.data.api.dto.City
@@ -56,7 +56,7 @@ class DetailStoreAddressActivity : AppCompatActivity() {
binding.tvError.visibility = View.GONE
// Set up header title
- binding.header.headerTitle.text = "Alamat Toko"
+ binding.header.headerTitle.text = "Atur Alamat Toko"
// Set up back button
binding.header.headerLeftIcon.setOnClickListener {
@@ -182,6 +182,9 @@ class DetailStoreAddressActivity : AppCompatActivity() {
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
+ binding.edtLatitude.setText(lat.toString())
+ binding.edtLongitude.setText(lng.toString())
+
// Set selected province ID to trigger city loading
if (address.provinceId.isNotEmpty()) {
selectedProvinceId = address.provinceId
@@ -271,8 +274,8 @@ class DetailStoreAddressActivity : AppCompatActivity() {
val subdistrict = binding.edtSubdistrict.text.toString()
val detail = binding.edtDetailAddress.text.toString()
val postalCode = binding.edtPostalCode.text.toString()
- val latitudeStr = TODO()
- val longitudeStr = TODO()
+ val latitudeStr = binding.edtLatitude.text.toString()
+ val longitudeStr = binding.edtLongitude.text.toString()
// Validate required fields
if (selectedProvinceId == null || binding.spinnerCity.selectedItemPosition <= 0 ||
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/payment_info/PaymentInfoActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/payment_info/PaymentInfoActivity.kt
index ad7c97d..bcb692a 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/payment_info/PaymentInfoActivity.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/payment_info/PaymentInfoActivity.kt
@@ -1,21 +1,282 @@
package com.alya.ecommerce_serang.ui.profile.mystore.profile.payment_info
+import android.app.Activity
+import android.app.AlertDialog
+import android.net.Uri
import android.os.Bundle
-import androidx.activity.enableEdgeToEdge
+import android.util.Log
+import android.view.View
+import android.widget.Button
+import android.widget.EditText
+import android.widget.ImageView
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
-import androidx.core.view.ViewCompat
-import androidx.core.view.WindowInsetsCompat
+import androidx.recyclerview.widget.LinearLayoutManager
import com.alya.ecommerce_serang.R
+import com.alya.ecommerce_serang.data.api.dto.PaymentInfo
+import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
+import com.alya.ecommerce_serang.data.repository.PaymentInfoRepository
+import com.alya.ecommerce_serang.databinding.ActivityPaymentInfoBinding
+import com.alya.ecommerce_serang.utils.BaseViewModelFactory
+import com.alya.ecommerce_serang.utils.SessionManager
+import com.alya.ecommerce_serang.utils.UriToFileConverter
+import com.alya.ecommerce_serang.utils.viewmodel.PaymentInfoViewModel
+import com.google.android.material.snackbar.Snackbar
+import java.io.File
class PaymentInfoActivity : AppCompatActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- enableEdgeToEdge()
- setContentView(R.layout.activity_payment_info)
- 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 val TAG = "PaymentInfoActivity"
+ private lateinit var binding: ActivityPaymentInfoBinding
+ private lateinit var adapter: PaymentInfoAdapter
+ private lateinit var sessionManager: SessionManager
+ private var selectedQrisImageUri: Uri? = null
+ private var selectedQrisImageFile: File? = null
+
+ // Store form data between dialog reopenings
+ private var savedBankName: String = ""
+ private var savedBankNumber: String = ""
+ private var savedAccountName: String = ""
+
+ private val viewModel: PaymentInfoViewModel by viewModels {
+ BaseViewModelFactory {
+ val apiService = ApiConfig.getApiService(sessionManager)
+ val repository = PaymentInfoRepository(apiService)
+ PaymentInfoViewModel(repository)
}
}
+
+ private val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
+ uri?.let {
+ try {
+ Log.d(TAG, "Selected image URI: $uri")
+ selectedQrisImageUri = it
+
+ // Convert URI to File
+ selectedQrisImageFile = UriToFileConverter.uriToFile(it, this)
+
+ if (selectedQrisImageFile == null) {
+ Log.e(TAG, "Failed to convert URI to file")
+ showSnackbar("Failed to process image. Please try another image.")
+ return@let
+ }
+
+ Log.d(TAG, "Converted to file: ${selectedQrisImageFile?.absolutePath}, size: ${selectedQrisImageFile?.length()} bytes")
+
+ // Check if file exists and has content
+ if (!selectedQrisImageFile!!.exists() || selectedQrisImageFile!!.length() == 0L) {
+ Log.e(TAG, "File doesn't exist or is empty: ${selectedQrisImageFile?.absolutePath}")
+ showSnackbar("Failed to process image. Please try another image.")
+ selectedQrisImageFile = null
+ return@let
+ }
+
+ showAddPaymentDialog(true) // Reopen dialog with selected image
+ } catch (e: Exception) {
+ Log.e(TAG, "Error processing selected image", e)
+ showSnackbar("Error processing image: ${e.message}")
+ selectedQrisImageUri = null
+ selectedQrisImageFile = null
+ }
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = ActivityPaymentInfoBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ sessionManager = SessionManager(this)
+
+ // Configure header
+ binding.header.headerTitle.text = "Atur Metode Pembayaran"
+
+ binding.header.headerLeftIcon.setOnClickListener {
+ onBackPressedDispatcher.onBackPressed()
+ }
+
+ setupRecyclerView()
+ setupObservers()
+
+ binding.btnAddPayment.setOnClickListener {
+ // Clear saved values when opening a new dialog
+ savedBankName = ""
+ savedBankNumber = ""
+ savedAccountName = ""
+ selectedQrisImageUri = null
+ selectedQrisImageFile = null
+ showAddPaymentDialog(false)
+ }
+
+ // Load payment info
+ viewModel.getPaymentInfo()
+ }
+
+ private fun setupRecyclerView() {
+ adapter = PaymentInfoAdapter(
+ onDeleteClick = { paymentMethod ->
+ showDeleteConfirmationDialog(paymentMethod)
+ }
+ )
+ binding.rvPaymentInfo.layoutManager = LinearLayoutManager(this)
+ binding.rvPaymentInfo.adapter = adapter
+ }
+
+ private fun setupObservers() {
+ viewModel.paymentInfos.observe(this) { paymentInfo ->
+ binding.progressBar.visibility = View.GONE
+
+ if (paymentInfo.isEmpty()) {
+ binding.tvEmptyState.visibility = View.VISIBLE
+ binding.rvPaymentInfo.visibility = View.GONE
+ } else {
+ binding.tvEmptyState.visibility = View.GONE
+ binding.rvPaymentInfo.visibility = View.VISIBLE
+ adapter.submitList(paymentInfo)
+ }
+ }
+
+ viewModel.isLoading.observe(this) { isLoading ->
+ binding.progressBar.visibility = if (isLoading) View.VISIBLE else View.GONE
+ binding.btnAddPayment.isEnabled = !isLoading
+ }
+
+ viewModel.errorMessage.observe(this) { errorMessage ->
+ if (errorMessage.isNotEmpty()) {
+ showSnackbar(errorMessage)
+ Log.e(TAG, "Error: $errorMessage")
+ }
+ }
+
+ viewModel.addPaymentSuccess.observe(this) { success ->
+ if (success) {
+ showSnackbar("Metode pembayaran berhasil ditambahkan")
+ setResult(Activity.RESULT_OK)
+ }
+ }
+
+ viewModel.deletePaymentSuccess.observe(this) { success ->
+ if (success) {
+ showSnackbar("Metode pembayaran berhasil dihapus")
+ setResult(Activity.RESULT_OK)
+ }
+ }
+ }
+
+ private fun showSnackbar(message: String) {
+ Snackbar.make(binding.root, message, Snackbar.LENGTH_LONG).show()
+ }
+
+ private fun showAddPaymentDialog(isReopened: Boolean) {
+ val builder = AlertDialog.Builder(this)
+ val dialogView = layoutInflater.inflate(R.layout.dialog_add_payment_info, null)
+ builder.setView(dialogView)
+
+ val dialog = builder.create()
+
+ // Get references to views in the dialog
+ val btnAddQris = dialogView.findViewById
+
+
\ 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 133e8f4..d3753bc 100644
--- a/app/src/main/res/layout/activity_balance_top_up.xml
+++ b/app/src/main/res/layout/activity_balance_top_up.xml
@@ -203,48 +203,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/activity_detail_store_address.xml b/app/src/main/res/layout/activity_detail_store_address.xml
index 837e0e5..b10e952 100644
--- a/app/src/main/res/layout/activity_detail_store_address.xml
+++ b/app/src/main/res/layout/activity_detail_store_address.xml
@@ -175,6 +175,7 @@
android:background="@drawable/bg_text_field"
android:padding="8dp"
style="@style/body_small"
+ android:hint="Isi nama jalan di sini"
android:layout_marginTop="10dp"/>
@@ -200,6 +201,7 @@
android:background="@drawable/bg_text_field"
android:padding="8dp"
style="@style/body_small"
+ android:hint="Isi nama kecamatan di sini"
android:layout_marginTop="10dp"/>
@@ -225,6 +227,7 @@
android:background="@drawable/bg_text_field"
android:padding="8dp"
style="@style/body_small"
+ android:hint="Isi kode pos di sini"
android:layout_marginTop="10dp"/>
@@ -258,31 +261,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:orientation="horizontal"
+ android:layout_marginBottom="16dp">
-
+
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:layout_marginEnd="8dp">
-
-
+
-
+
+
+
+
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:layout_marginStart="8dp">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_shipping_service.xml b/app/src/main/res/layout/activity_shipping_service.xml
index c283adf..59ce7b2 100644
--- a/app/src/main/res/layout/activity_shipping_service.xml
+++ b/app/src/main/res/layout/activity_shipping_service.xml
@@ -2,10 +2,130 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_add_payment_info.xml b/app/src/main/res/layout/dialog_add_payment_info.xml
new file mode 100644
index 0000000..4993763
--- /dev/null
+++ b/app/src/main/res/layout/dialog_add_payment_info.xml
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_payment_info.xml b/app/src/main/res/layout/item_payment_info.xml
new file mode 100644
index 0000000..a36f8e0
--- /dev/null
+++ b/app/src/main/res/layout/item_payment_info.xml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
From 654a415ddec98fbde1caa6376f8f155d37c34639 Mon Sep 17 00:00:00 2001
From: Gracia Hotmauli <95269134+hotmauligracia@users.noreply.github.com>
Date: Tue, 13 May 2025 20:10:34 +0700
Subject: [PATCH 4/4] edit store profile & topup
---
app/src/main/AndroidManifest.xml | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index fa26332..39f8344 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -52,7 +52,9 @@
android:enabled="true"
android:exported="false"
android:foregroundServiceType="dataSync" />
-
+