mirror of
https://github.com/shaulascr/ecommerce_serang.git
synced 2025-08-10 09:22:21 +00:00
Merge branch 'master' into screen-features
# Conflicts: # app/src/main/AndroidManifest.xml # app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt # app/src/main/java/com/alya/ecommerce_serang/data/repository/OrderRepository.kt # app/src/main/java/com/alya/ecommerce_serang/ui/order/CheckoutActivity.kt
This commit is contained in:
8
.idea/deploymentTargetSelector.xml
generated
8
.idea/deploymentTargetSelector.xml
generated
@ -4,6 +4,14 @@
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DropdownSelection timestamp="2025-05-08T14:50:55.425322500Z">
|
||||
<Target type="DEFAULT_BOOT">
|
||||
<handle>
|
||||
<DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\Gracia Hotmauli\.android\avd\Pixel_8_2_2.avd" />
|
||||
</handle>
|
||||
</Target>
|
||||
</DropdownSelection>
|
||||
<DialogSelection />
|
||||
</SelectionState>
|
||||
</selectionStates>
|
||||
</component>
|
||||
|
@ -41,6 +41,18 @@
|
||||
<activity
|
||||
android:name=".ui.cart.MainActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.profile.mystore.profile.EditStoreProfileActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.profile.mystore.sells.shipment.DetailShipmentActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.profile.mystore.sells.payment.DetailPaymentActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.profile.mystore.sells.order.DetailOrderActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.chat.ChatActivity"
|
||||
android:exported="false" /> <!-- <provider -->
|
||||
@ -52,7 +64,9 @@
|
||||
android:enabled="true"
|
||||
android:exported="false"
|
||||
android:foregroundServiceType="dataSync" />
|
||||
|
||||
<activity
|
||||
android:name=".ui.profile.mystore.profile.shipping_service.ShippingServiceActivity"
|
||||
android:exported="false"/>
|
||||
<activity
|
||||
android:name=".ui.notif.NotificationActivity"
|
||||
android:exported="false" />
|
||||
@ -104,12 +118,6 @@
|
||||
<activity
|
||||
android:name=".ui.profile.mystore.balance.BalanceActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.profile.mystore.sells.shipment.ShippingConfirmationActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.profile.mystore.sells.payment.ClaimPaymentActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.profile.mystore.balance.BalanceTopUpActivity"
|
||||
android:exported="false" />
|
||||
|
@ -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<City>
|
||||
)
|
@ -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<PaymentInfo>
|
||||
)
|
||||
|
||||
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?
|
||||
)
|
@ -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<Province>
|
||||
)
|
@ -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<String>
|
||||
)
|
@ -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
|
||||
)
|
@ -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
|
||||
)
|
@ -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(
|
||||
|
@ -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<OrderItemsItem?>? = 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
|
||||
)
|
||||
|
@ -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<UpdatedItemsItem?>? = 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<ItemsItem?>? = 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
|
||||
)
|
||||
|
@ -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
|
||||
)
|
@ -0,0 +1,56 @@
|
||||
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? = null,
|
||||
val shipping: List<Shipping>? = emptyList(),
|
||||
val payment: List<Payment> = emptyList()
|
||||
)
|
||||
|
||||
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?,
|
||||
@SerializedName("account_name") val accountName: String?
|
||||
)
|
@ -0,0 +1,6 @@
|
||||
package com.alya.ecommerce_serang.data.api.response.store.topup
|
||||
|
||||
data class BalanceTopUpResponse(
|
||||
val success: Boolean,
|
||||
val message: String
|
||||
)
|
@ -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<TopUp>
|
||||
)
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,9 @@ 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
|
||||
import com.alya.ecommerce_serang.data.api.dto.CourierCostRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.CreateAddressRequest
|
||||
@ -10,9 +12,12 @@ 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.ReviewProductItem
|
||||
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
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.LoginResponse
|
||||
@ -52,6 +57,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
|
||||
@ -161,7 +170,16 @@ interface ApiService {
|
||||
): Response<CreateAddressResponse>
|
||||
|
||||
@GET("mystore")
|
||||
suspend fun getStore (): Response<StoreResponse>
|
||||
suspend fun getStore(): Response<StoreResponse>
|
||||
|
||||
@GET("mystore")
|
||||
suspend fun getStoreData(): Response<StoreDataResponse>
|
||||
|
||||
@GET("mystore")
|
||||
suspend fun getMyStoreData(): Response<com.alya.ecommerce_serang.data.api.response.store.StoreResponse>
|
||||
|
||||
@GET("mystore")
|
||||
suspend fun getStoreAddress(): Response<StoreAddressResponse>
|
||||
|
||||
@GET("mystore/product") // Replace with actual endpoint
|
||||
suspend fun getStoreProduct(): Response<ViewStoreProductsResponse>
|
||||
@ -231,19 +249,22 @@ interface ApiService {
|
||||
suspend fun getListProv(
|
||||
): Response<ListProvinceResponse>
|
||||
|
||||
@GET("mystore/orders")
|
||||
suspend fun getAllOrders(): Response<OrderListResponse>
|
||||
|
||||
@GET("mystore/orders/{status}")
|
||||
suspend fun getOrdersByStatus(
|
||||
@Query("status") status: String
|
||||
): Response<OrderListResponse>
|
||||
@GET("order/{status}")
|
||||
suspend fun getSellList(
|
||||
@Path("status") status: String
|
||||
): Response<com.alya.ecommerce_serang.data.api.response.store.orders.OrderListResponse>
|
||||
|
||||
@PUT("store/order/update")
|
||||
suspend fun confirmOrder(
|
||||
@Body confirmOrder : CompletedOrderRequest
|
||||
): Response<CompletedOrderResponse>
|
||||
|
||||
@PUT("store/order/update")
|
||||
suspend fun updateOrder(
|
||||
@Query("order_id") orderId: Int?,
|
||||
@Query("status") status: String
|
||||
): Response<com.alya.ecommerce_serang.data.api.response.store.orders.UpdateOrderItemResponse>
|
||||
|
||||
@Multipart
|
||||
@POST("addcomplaint")
|
||||
suspend fun addComplaint(
|
||||
@ -257,6 +278,89 @@ interface ApiService {
|
||||
@Body contentReview : ReviewProductItem
|
||||
): Response<CreateReviewResponse>
|
||||
|
||||
@GET("store/topup")
|
||||
suspend fun getTopUpHistory(): Response<TopUpResponse>
|
||||
|
||||
@GET("store/topup")
|
||||
suspend fun getFilteredTopUpHistory(@Query("date") date: String): Response<TopUpResponse>
|
||||
|
||||
@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<BalanceTopUpResponse>
|
||||
|
||||
@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<StoreDataResponse>
|
||||
|
||||
@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<GenericResponse>
|
||||
|
||||
@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<AddPaymentInfoResponse>
|
||||
|
||||
@DELETE("mystore/payment/delete/{id}")
|
||||
suspend fun deletePaymentInfo(
|
||||
@Path("id") paymentMethodId: Int
|
||||
): Response<GenericResponse>
|
||||
|
||||
// Shipping Service API endpoints
|
||||
@POST("mystore/shipping/add")
|
||||
suspend fun addShippingService(
|
||||
@Body request: ShippingServiceRequest
|
||||
): Response<GenericResponse>
|
||||
|
||||
@POST("mystore/shipping/delete")
|
||||
suspend fun deleteShippingService(
|
||||
@Body request: ShippingServiceRequest
|
||||
): Response<GenericResponse>
|
||||
|
||||
@GET("provinces")
|
||||
suspend fun getProvinces(): Response<ProvinceResponse>
|
||||
|
||||
@GET("cities/{provinceId}")
|
||||
suspend fun getCities(
|
||||
@Path("provinceId") provinceId: String
|
||||
): Response<CityResponse>
|
||||
|
||||
@PUT("mystore/edit")
|
||||
suspend fun updateStoreAddress(
|
||||
@Body addressData: HashMap<String, Any?>
|
||||
): Response<StoreAddressResponse>
|
||||
|
||||
@POST("search")
|
||||
suspend fun saveSearchQuery(
|
||||
@Body searchRequest: SearchRequest
|
||||
|
@ -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<Province> = 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<City> = 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<String, Any?>()
|
||||
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}")
|
||||
}
|
||||
}
|
||||
}
|
@ -293,35 +293,6 @@ class OrderRepository(private val apiService: ApiService) {
|
||||
return if (response.isSuccessful) response.body() else null
|
||||
}
|
||||
|
||||
suspend fun fetchSells(): List<OrdersItem> {
|
||||
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<OrdersItem?> {
|
||||
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<UserProfile?> {
|
||||
return try {
|
||||
val response = apiService.getUserProfile()
|
||||
|
@ -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<PaymentInfo> = 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<PaymentInfo>()
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package com.alya.ecommerce_serang.data.repository
|
||||
|
||||
import android.util.Log
|
||||
import com.alya.ecommerce_serang.data.api.response.store.orders.OrderListResponse
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiService
|
||||
|
||||
class SellsRepository(private val apiService: ApiService) {
|
||||
suspend fun getSellList(status: String): Result<OrderListResponse> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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<String> = 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<String>): 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<String>): 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
|
||||
}
|
||||
}
|
||||
}
|
@ -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,10 +18,8 @@ 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.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
|
||||
import com.alya.ecommerce_serang.utils.viewmodel.MyStoreViewModel
|
||||
@ -67,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")
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,31 +91,26 @@ class MyStoreActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
binding.tvHistory.setOnClickListener {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(android.R.id.content, AllSellsFragment())
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
val intent = Intent(this, SellsActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
binding.layoutPerluTagihan.setOnClickListener {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(android.R.id.content, OrderFragment())
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
val intent = Intent(this, SellsActivity::class.java)
|
||||
startActivity(intent)
|
||||
//navigateToSellsFragment("pending")
|
||||
}
|
||||
|
||||
binding.layoutPembayaran.setOnClickListener {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(android.R.id.content, PaymentFragment())
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
val intent = Intent(this, SellsActivity::class.java)
|
||||
startActivity(intent)
|
||||
//navigateToSellsFragment("paid")
|
||||
}
|
||||
|
||||
binding.layoutPerluDikirim.setOnClickListener {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(android.R.id.content, ShipmentFragment())
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
val intent = Intent(this, SellsActivity::class.java)
|
||||
startActivity(intent)
|
||||
//navigateToSellsFragment("processed")
|
||||
}
|
||||
|
||||
binding.layoutProductMenu.setOnClickListener {
|
||||
@ -129,4 +131,25 @@ class MyStoreActivity : AppCompatActivity() {
|
||||
.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()
|
||||
// }
|
||||
}
|
@ -1,21 +1,414 @@
|
||||
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<TopUp> = emptyList()
|
||||
|
||||
private val TAG = "BalanceActivity"
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// 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)
|
||||
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<TopUp>) {
|
||||
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
|
||||
}
|
||||
}
|
@ -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<Payment> = 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<TextView>(R.id.header_title)
|
||||
headerTitle.text = "Isi Ulang Saldo"
|
||||
|
||||
// Setup back button
|
||||
val backButton = findViewById<ImageView>(R.id.header_left_icon)
|
||||
backButton.setOnClickListener {
|
||||
onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
@ -1,7 +1,90 @@
|
||||
package com.alya.ecommerce_serang.ui.profile.mystore.balance
|
||||
|
||||
/* class BalanceTransactionAdapter(private val balanceTransactionList: List<BalanceTransaction>) :
|
||||
RecyclerView.Adapter<BalanceTransactionAdapter.TransactionViewHolder>() {
|
||||
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<TopUp, BalanceTransactionViewHolder>(DIFF_CALLBACK) {
|
||||
|
||||
}*/
|
||||
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<TopUp>() {
|
||||
override fun areItemsTheSame(oldItem: TopUp, newItem: TopUp): Boolean {
|
||||
return oldItem.id == newItem.id
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: TopUp, newItem: TopUp): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,15 +1,22 @@
|
||||
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.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
|
||||
@ -39,6 +46,36 @@ 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)
|
||||
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()
|
||||
|
||||
viewModel.myStoreProfile.observe(this){ user ->
|
||||
@ -50,11 +87,64 @@ 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)
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
|
||||
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){
|
||||
// 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")
|
||||
}
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
@ -1,21 +1,315 @@
|
||||
package com.alya.ecommerce_serang.ui.profile.mystore.profile.address
|
||||
|
||||
import android.app.Activity
|
||||
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.AlertDialog
|
||||
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<Province> = emptyList()
|
||||
private var cities: List<City> = 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 = "Atur 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
|
||||
|
||||
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
|
||||
|
||||
// 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 = binding.edtLatitude.text.toString()
|
||||
val longitudeStr = binding.edtLongitude.text.toString()
|
||||
|
||||
// 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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -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<Button>(R.id.btn_add_qris)
|
||||
val bankNameEditText = dialogView.findViewById<EditText>(R.id.edt_bank_name)
|
||||
val bankNumberEditText = dialogView.findViewById<EditText>(R.id.edt_bank_number)
|
||||
val accountNameEditText = dialogView.findViewById<EditText>(R.id.edt_account_name)
|
||||
val qrisPreview = dialogView.findViewById<ImageView>(R.id.iv_qris_preview)
|
||||
val btnCancel = dialogView.findViewById<Button>(R.id.btn_cancel)
|
||||
val btnSave = dialogView.findViewById<Button>(R.id.btn_save)
|
||||
|
||||
// When reopening, restore the previously entered values
|
||||
if (isReopened) {
|
||||
bankNameEditText.setText(savedBankName)
|
||||
bankNumberEditText.setText(savedBankNumber)
|
||||
accountNameEditText.setText(savedAccountName)
|
||||
|
||||
if (selectedQrisImageUri != null) {
|
||||
Log.d(TAG, "Showing selected QRIS image: $selectedQrisImageUri")
|
||||
qrisPreview.setImageURI(selectedQrisImageUri)
|
||||
qrisPreview.visibility = View.VISIBLE
|
||||
showSnackbar("Gambar QRIS berhasil dipilih")
|
||||
}
|
||||
}
|
||||
|
||||
btnAddQris.setOnClickListener {
|
||||
// Save the current values before dismissing
|
||||
savedBankName = bankNameEditText.text.toString().trim()
|
||||
savedBankNumber = bankNumberEditText.text.toString().trim()
|
||||
savedAccountName = accountNameEditText.text.toString().trim()
|
||||
|
||||
getContent.launch("image/*")
|
||||
dialog.dismiss() // Dismiss the current dialog as we'll reopen it
|
||||
}
|
||||
|
||||
btnCancel.setOnClickListener {
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
btnSave.setOnClickListener {
|
||||
val bankName = bankNameEditText.text.toString().trim()
|
||||
val bankNumber = bankNumberEditText.text.toString().trim()
|
||||
val accountName = accountNameEditText.text.toString().trim()
|
||||
|
||||
// Validation
|
||||
if (bankName.isEmpty()) {
|
||||
showSnackbar("Nama bank tidak boleh kosong")
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
if (bankNumber.isEmpty()) {
|
||||
showSnackbar("Nomor rekening tidak boleh kosong")
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
if (accountName.isEmpty()) {
|
||||
showSnackbar("Nama pemilik rekening tidak boleh kosong")
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
if (bankNumber.any { !it.isDigit() }) {
|
||||
showSnackbar("Nomor rekening hanya boleh berisi angka")
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
// Log the data being sent
|
||||
Log.d(TAG, "====== SENDING PAYMENT METHOD DATA ======")
|
||||
Log.d(TAG, "Bank Name: $bankName")
|
||||
Log.d(TAG, "Bank Number: $bankNumber")
|
||||
Log.d(TAG, "Account Name: $accountName")
|
||||
if (selectedQrisImageFile != null) {
|
||||
Log.d(TAG, "QRIS file path: ${selectedQrisImageFile?.absolutePath}")
|
||||
Log.d(TAG, "QRIS file exists: ${selectedQrisImageFile?.exists()}")
|
||||
Log.d(TAG, "QRIS file size: ${selectedQrisImageFile?.length()} bytes")
|
||||
} else {
|
||||
Log.d(TAG, "No QRIS file selected")
|
||||
}
|
||||
|
||||
// Temporarily disable the save button
|
||||
btnSave.isEnabled = false
|
||||
btnSave.text = "Menyimpan..."
|
||||
|
||||
// Add payment info
|
||||
viewModel.addPaymentInfo(
|
||||
bankName = bankName,
|
||||
bankNumber = bankNumber,
|
||||
accountName = accountName,
|
||||
qrisImageUri = selectedQrisImageUri,
|
||||
qrisImageFile = selectedQrisImageFile
|
||||
)
|
||||
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
private fun showDeleteConfirmationDialog(paymentInfo: PaymentInfo) {
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle("Hapus Metode Pembayaran")
|
||||
.setMessage("Apakah Anda yakin ingin menghapus metode pembayaran ini?")
|
||||
.setPositiveButton("Hapus") { _, _ ->
|
||||
viewModel.deletePaymentInfo(paymentInfo.id)
|
||||
}
|
||||
.setNegativeButton("Batal", null)
|
||||
.show()
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package com.alya.ecommerce_serang.ui.profile.mystore.profile.payment_info
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
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.dto.PaymentInfo
|
||||
import com.bumptech.glide.Glide
|
||||
|
||||
class PaymentInfoAdapter(
|
||||
private val onDeleteClick: (PaymentInfo) -> Unit
|
||||
) : ListAdapter<PaymentInfo, PaymentInfoAdapter.PaymentInfoViewHolder>(DIFF_CALLBACK) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PaymentInfoViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.item_payment_info, parent, false)
|
||||
return PaymentInfoViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: PaymentInfoViewHolder, position: Int) {
|
||||
holder.bind(getItem(position))
|
||||
}
|
||||
|
||||
inner class PaymentInfoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
private val tvBankName: TextView = itemView.findViewById(R.id.tv_bank_name)
|
||||
private val tvAccountName: TextView = itemView.findViewById(R.id.tv_account_name)
|
||||
private val tvBankNumber: TextView = itemView.findViewById(R.id.tv_bank_number)
|
||||
private val ivDelete: ImageView = itemView.findViewById(R.id.iv_delete)
|
||||
private val layoutQris: LinearLayout = itemView.findViewById(R.id.layout_qris)
|
||||
private val ivQris: ImageView = itemView.findViewById(R.id.iv_qris)
|
||||
|
||||
fun bind(paymentInfo: PaymentInfo) {
|
||||
tvBankName.text = paymentInfo.bankName
|
||||
tvAccountName.text = paymentInfo.accountName ?: ""
|
||||
tvBankNumber.text = paymentInfo.bankNum
|
||||
|
||||
// Handle QRIS image if available
|
||||
if (paymentInfo.qrisImage != null && paymentInfo.qrisImage.isNotEmpty() && paymentInfo.qrisImage != "null") {
|
||||
layoutQris.visibility = View.VISIBLE
|
||||
// Make sure the URL is correct by handling both relative and absolute paths
|
||||
val imageUrl = if (paymentInfo.qrisImage.startsWith("http")) {
|
||||
paymentInfo.qrisImage
|
||||
} else {
|
||||
"http://192.168.100.156:3000${paymentInfo.qrisImage}"
|
||||
}
|
||||
|
||||
Log.d("PaymentMethodAdapter", "Loading QRIS image from: $imageUrl")
|
||||
|
||||
Glide.with(itemView.context)
|
||||
.load(imageUrl)
|
||||
.placeholder(R.drawable.placeholder_image)
|
||||
.error(R.drawable.placeholder_image)
|
||||
.into(ivQris)
|
||||
} else {
|
||||
layoutQris.visibility = View.GONE
|
||||
}
|
||||
|
||||
ivDelete.setOnClickListener {
|
||||
onDeleteClick(paymentInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<PaymentInfo>() {
|
||||
override fun areItemsTheSame(oldItem: PaymentInfo, newItem: PaymentInfo): Boolean {
|
||||
return oldItem.id == newItem.id
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: PaymentInfo, newItem: PaymentInfo): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,21 +1,108 @@
|
||||
package com.alya.ecommerce_serang.ui.profile.mystore.profile.shipping_service
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import android.view.View
|
||||
import android.widget.CheckBox
|
||||
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.data.api.retrofit.ApiConfig
|
||||
import com.alya.ecommerce_serang.data.repository.ShippingServiceRepository
|
||||
import com.alya.ecommerce_serang.databinding.ActivityShippingServiceBinding
|
||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||
import com.alya.ecommerce_serang.utils.SessionManager
|
||||
import com.alya.ecommerce_serang.utils.viewmodel.ShippingServiceViewModel
|
||||
|
||||
class ShippingServiceActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContentView(R.layout.activity_shipping_service)
|
||||
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 binding: ActivityShippingServiceBinding
|
||||
private lateinit var sessionManager: SessionManager
|
||||
private val courierCheckboxes = mutableListOf<Pair<CheckBox, String>>()
|
||||
|
||||
private val viewModel: ShippingServiceViewModel by viewModels {
|
||||
BaseViewModelFactory {
|
||||
val apiService = ApiConfig.getApiService(sessionManager)
|
||||
val repository = ShippingServiceRepository(apiService)
|
||||
ShippingServiceViewModel(repository)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityShippingServiceBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
sessionManager = SessionManager(this)
|
||||
|
||||
// Configure header
|
||||
binding.header.headerTitle.text = "Atur Layanan Pengiriman"
|
||||
|
||||
binding.header.headerLeftIcon.setOnClickListener {
|
||||
onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
|
||||
setupCourierCheckboxes()
|
||||
setupObservers()
|
||||
|
||||
binding.btnSave.setOnClickListener {
|
||||
saveShippingServices()
|
||||
}
|
||||
|
||||
// Load shipping services
|
||||
viewModel.getAvailableCouriers()
|
||||
}
|
||||
|
||||
private fun setupCourierCheckboxes() {
|
||||
// Add all courier checkboxes to the list for easy management
|
||||
courierCheckboxes.add(Pair(binding.checkboxJne, "jne"))
|
||||
courierCheckboxes.add(Pair(binding.checkboxPos, "pos"))
|
||||
courierCheckboxes.add(Pair(binding.checkboxTiki, "tiki"))
|
||||
}
|
||||
|
||||
private fun setupObservers() {
|
||||
viewModel.availableCouriers.observe(this) { couriers ->
|
||||
// Check the appropriate checkboxes based on available couriers
|
||||
for (pair in courierCheckboxes) {
|
||||
val checkbox = pair.first
|
||||
val courierCode = pair.second
|
||||
checkbox.isChecked = couriers.contains(courierCode)
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.isLoading.observe(this) { isLoading ->
|
||||
binding.progressBar.visibility = if (isLoading) View.VISIBLE else View.GONE
|
||||
binding.contentLayout.visibility = if (isLoading) View.GONE else View.VISIBLE
|
||||
}
|
||||
|
||||
viewModel.errorMessage.observe(this) { errorMessage ->
|
||||
Toast.makeText(this, errorMessage, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
viewModel.saveSuccess.observe(this) { success ->
|
||||
if (success) {
|
||||
Toast.makeText(this, "Layanan pengiriman berhasil disimpan", Toast.LENGTH_SHORT).show()
|
||||
setResult(Activity.RESULT_OK)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveShippingServices() {
|
||||
val selectedCouriers = mutableListOf<String>()
|
||||
|
||||
for (pair in courierCheckboxes) {
|
||||
val checkbox = pair.first
|
||||
val courierCode = pair.second
|
||||
if (checkbox.isChecked) {
|
||||
selectedCouriers.add(courierCode)
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedCouriers.isEmpty()) {
|
||||
Toast.makeText(this, "Pilih minimal satu layanan pengiriman", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
viewModel.saveShippingServices(selectedCouriers)
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
@ -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<RecyclerView.ViewHolder>() {
|
||||
private var orderList: List<OrdersItem?> = emptyList()
|
||||
class SellsAdapter(
|
||||
private val onOrderClickListener: (OrdersItem) -> Unit,
|
||||
private val viewModel: SellsViewModel
|
||||
) : RecyclerView.Adapter<SellsAdapter.SellsViewHolder>() {
|
||||
|
||||
// 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<OrdersItem>()
|
||||
private var fragmentStatus: String = "all"
|
||||
|
||||
// Method to submit list to the adapter
|
||||
fun submitList(orders: List<OrdersItem?>?) {
|
||||
orderList = orders ?: emptyList()
|
||||
fun setFragmentStatus(status: String) {
|
||||
fragmentStatus = status
|
||||
}
|
||||
|
||||
fun submitList(newSells: List<OrdersItem>) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
@ -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])
|
||||
}
|
||||
}
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
@ -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)
|
||||
// })
|
||||
}
|
||||
}
|
@ -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)
|
||||
// })
|
||||
}
|
||||
}
|
@ -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)
|
||||
// })
|
||||
}
|
||||
}
|
@ -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)
|
||||
// })
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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<OrderAdapter.OrderViewHolder>() {
|
||||
|
||||
private var orderList: List<OrdersItem?>? = emptyList()
|
||||
|
||||
fun submitList(orders: List<OrdersItem?>?) {
|
||||
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}"
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
@ -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)
|
@ -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<PaymentAdapter.PaymentViewHolder>() {
|
||||
|
||||
private var paymentList: List<OrdersItem?>? = emptyList()
|
||||
|
||||
fun submitList(orders: List<OrdersItem?>?) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
@ -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)
|
@ -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<ShipmentAdapter.ShipmentViewHolder>() {
|
||||
|
||||
private var shipmentList: List<OrdersItem> = emptyList()
|
||||
|
||||
fun submitList(orders: List<OrdersItem>) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
// })
|
||||
}
|
||||
}
|
@ -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)
|
||||
// })
|
||||
}
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
package com.alya.ecommerce_serang.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.provider.MediaStore
|
||||
import android.provider.OpenableColumns
|
||||
import android.util.Log
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import kotlin.random.Random
|
||||
|
||||
object UriToFileConverter {
|
||||
private const val TAG = "UriToFileConverter"
|
||||
|
||||
fun uriToFile(uri: Uri, context: Context): File? {
|
||||
return try {
|
||||
Log.d(TAG, "Converting URI to file: $uri")
|
||||
|
||||
// Try to get original filename
|
||||
val fileName = getFileNameFromUri(uri, context) ?: "upload_${System.currentTimeMillis()}"
|
||||
val extension = getFileExtension(fileName) ?: ".jpg"
|
||||
|
||||
// Create a temporary file in the cache directory with proper name
|
||||
val tempFile = File.createTempFile(
|
||||
"upload_${Random.nextInt(10000)}",
|
||||
extension,
|
||||
context.cacheDir
|
||||
)
|
||||
|
||||
Log.d(TAG, "Created temp file: ${tempFile.absolutePath}")
|
||||
|
||||
// Open the input stream and copy content
|
||||
var inputStream: InputStream? = null
|
||||
try {
|
||||
inputStream = context.contentResolver.openInputStream(uri)
|
||||
if (inputStream == null) {
|
||||
Log.e(TAG, "Failed to open input stream for URI: $uri")
|
||||
return null
|
||||
}
|
||||
|
||||
// Copy content using a buffer
|
||||
val outputStream = FileOutputStream(tempFile)
|
||||
val buffer = ByteArray(4 * 1024) // 4 KB buffer
|
||||
var bytesRead: Int
|
||||
var totalBytesRead = 0
|
||||
|
||||
while (inputStream.read(buffer).also { bytesRead = it } != -1) {
|
||||
outputStream.write(buffer, 0, bytesRead)
|
||||
totalBytesRead += bytesRead
|
||||
}
|
||||
|
||||
outputStream.flush()
|
||||
outputStream.close()
|
||||
|
||||
Log.d(TAG, "Successfully copied $totalBytesRead bytes to file")
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error copying file data", e)
|
||||
return null
|
||||
} finally {
|
||||
inputStream?.close()
|
||||
}
|
||||
|
||||
// Verify the file
|
||||
if (!tempFile.exists() || tempFile.length() == 0L) {
|
||||
Log.e(TAG, "Created file doesn't exist or is empty: ${tempFile.absolutePath}")
|
||||
return null
|
||||
}
|
||||
|
||||
Log.d(TAG, "Successfully converted URI to file: ${tempFile.absolutePath}, size: ${tempFile.length()} bytes")
|
||||
tempFile
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error converting URI to file", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFileNameFromUri(uri: Uri, context: Context): String? {
|
||||
// Try the OpenableColumns query method first
|
||||
val cursor = context.contentResolver.query(uri, null, null, null, null)
|
||||
cursor?.use { c ->
|
||||
if (c.moveToFirst()) {
|
||||
val nameIndex = c.getColumnIndex(OpenableColumns.DISPLAY_NAME)
|
||||
if (nameIndex != -1) {
|
||||
val fileName = c.getString(nameIndex)
|
||||
Log.d(TAG, "Retrieved filename from OpenableColumns: $fileName")
|
||||
return fileName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try MediaStore method
|
||||
val projection = arrayOf(MediaStore.Images.Media.DISPLAY_NAME)
|
||||
try {
|
||||
context.contentResolver.query(uri, projection, null, null, null)?.use { c ->
|
||||
if (c.moveToFirst()) {
|
||||
val nameIndex = c.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)
|
||||
val fileName = c.getString(nameIndex)
|
||||
Log.d(TAG, "Retrieved filename from MediaStore: $fileName")
|
||||
return fileName
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error getting filename from MediaStore", e)
|
||||
}
|
||||
|
||||
// Last resort: extract from URI path
|
||||
uri.path?.let { path ->
|
||||
val fileName = path.substring(path.lastIndexOf('/') + 1)
|
||||
Log.d(TAG, "Retrieved filename from URI path: $fileName")
|
||||
return fileName
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getFileExtension(fileName: String): String? {
|
||||
val lastDot = fileName.lastIndexOf('.')
|
||||
return if (lastDot >= 0) {
|
||||
fileName.substring(lastDot)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun getFilePathFromUri(uri: Uri, context: Context): String? {
|
||||
// For Media Gallery
|
||||
val projection = arrayOf(MediaStore.Images.Media.DATA)
|
||||
try {
|
||||
val cursor = context.contentResolver.query(uri, projection, null, null, null)
|
||||
cursor?.use {
|
||||
if (it.moveToFirst()) {
|
||||
val columnIndex = it.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
|
||||
return it.getString(columnIndex)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error getting file path from URI", e)
|
||||
}
|
||||
|
||||
// If the above method fails, try direct conversion
|
||||
return uri.path
|
||||
}
|
||||
}
|
@ -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<List<Province>>()
|
||||
val provinces: LiveData<List<Province>> = _provinces
|
||||
|
||||
private val _cities = MutableLiveData<List<City>>()
|
||||
val cities: LiveData<List<City>> = _cities
|
||||
|
||||
private val _storeAddress = MutableLiveData<StoreAddress?>()
|
||||
val storeAddress: LiveData<StoreAddress?> = _storeAddress
|
||||
|
||||
private val _isLoading = MutableLiveData<Boolean>()
|
||||
val isLoading: LiveData<Boolean> = _isLoading
|
||||
|
||||
private val _errorMessage = MutableLiveData<String>()
|
||||
val errorMessage: LiveData<String> = _errorMessage
|
||||
|
||||
private val _saveSuccess = MutableLiveData<Boolean>()
|
||||
val saveSuccess: LiveData<Boolean> = _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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
package com.alya.ecommerce_serang.utils.viewmodel
|
||||
|
||||
import android.net.Uri
|
||||
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.PaymentInfo
|
||||
import com.alya.ecommerce_serang.data.repository.PaymentInfoRepository
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
|
||||
class PaymentInfoViewModel(private val repository: PaymentInfoRepository) : ViewModel() {
|
||||
|
||||
private val TAG = "PaymentInfoViewModel"
|
||||
|
||||
private val _paymentInfos = MutableLiveData<List<PaymentInfo>>()
|
||||
val paymentInfos: LiveData<List<PaymentInfo>> = _paymentInfos
|
||||
|
||||
private val _isLoading = MutableLiveData<Boolean>()
|
||||
val isLoading: LiveData<Boolean> = _isLoading
|
||||
|
||||
private val _errorMessage = MutableLiveData<String>()
|
||||
val errorMessage: LiveData<String> = _errorMessage
|
||||
|
||||
private val _addPaymentSuccess = MutableLiveData<Boolean>()
|
||||
val addPaymentSuccess: LiveData<Boolean> = _addPaymentSuccess
|
||||
|
||||
private val _deletePaymentSuccess = MutableLiveData<Boolean>()
|
||||
val deletePaymentSuccess: LiveData<Boolean> = _deletePaymentSuccess
|
||||
|
||||
fun getPaymentInfo() {
|
||||
_isLoading.value = true
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
Log.d(TAG, "Loading payment info...")
|
||||
val result = repository.getPaymentInfo()
|
||||
|
||||
if (result.isEmpty()) {
|
||||
Log.d(TAG, "No payment info found")
|
||||
} else {
|
||||
Log.d(TAG, "Successfully loaded ${result.size} payment info")
|
||||
for (method in result) {
|
||||
Log.d(TAG, "Payment method: id=${method.id}, bank=${method.bankName}, account=${method.accountName}")
|
||||
}
|
||||
}
|
||||
|
||||
_paymentInfos.value = result
|
||||
_isLoading.value = false
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error getting payment info", e)
|
||||
_errorMessage.value = "Gagal memuat metode pembayaran: ${e.message?.take(100) ?: "Unknown error"}"
|
||||
_isLoading.value = false
|
||||
// Still set empty payment info to show empty state
|
||||
_paymentInfos.value = emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun addPaymentInfo(bankName: String, bankNumber: String, accountName: String, qrisImageUri: Uri?, qrisImageFile: File?) {
|
||||
_isLoading.value = true
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
Log.d(TAG, "Adding payment info: bankName=$bankName, bankNumber=$bankNumber, accountName=$accountName")
|
||||
Log.d(TAG, "Image file: ${qrisImageFile?.absolutePath}, exists: ${qrisImageFile?.exists()}, size: ${qrisImageFile?.length() ?: 0} bytes")
|
||||
|
||||
// Validate the file if it was provided
|
||||
if (qrisImageUri != null && qrisImageFile == null) {
|
||||
_errorMessage.value = "Gagal memproses gambar. Silakan pilih gambar lain."
|
||||
_isLoading.value = false
|
||||
_addPaymentSuccess.value = false
|
||||
return@launch
|
||||
}
|
||||
|
||||
// If we have a file, make sure it exists and has some content
|
||||
if (qrisImageFile != null && (!qrisImageFile.exists() || qrisImageFile.length() == 0L)) {
|
||||
Log.e(TAG, "Image file does not exist or is empty: ${qrisImageFile.absolutePath}")
|
||||
_errorMessage.value = "File gambar tidak valid. Silakan pilih gambar lain."
|
||||
_isLoading.value = false
|
||||
_addPaymentSuccess.value = false
|
||||
return@launch
|
||||
}
|
||||
|
||||
val success = repository.addPaymentMethod(bankName, bankNumber, accountName, qrisImageFile)
|
||||
_addPaymentSuccess.value = success
|
||||
_isLoading.value = false
|
||||
|
||||
if (success) {
|
||||
// Refresh the payment info list
|
||||
getPaymentInfo()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error adding payment info", e)
|
||||
_errorMessage.value = "Gagal menambahkan metode pembayaran: ${e.message?.take(100) ?: "Unknown error"}"
|
||||
_isLoading.value = false
|
||||
_addPaymentSuccess.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun deletePaymentInfo(paymentInfoId: Int) {
|
||||
_isLoading.value = true
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val success = repository.deletePaymentMethod(paymentInfoId)
|
||||
_deletePaymentSuccess.value = success
|
||||
_isLoading.value = false
|
||||
if (success) {
|
||||
// Refresh the payment info list
|
||||
getPaymentInfo()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error deleting payment info", e)
|
||||
_errorMessage.value = "Gagal menghapus metode pembayaran: ${e.message?.take(100) ?: "Unknown error"}"
|
||||
_isLoading.value = false
|
||||
_deletePaymentSuccess.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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<List<OrdersItem?>>()
|
||||
val sellsList: LiveData<List<OrdersItem?>> get() = _sellsList
|
||||
class SellsViewModel(private val repository: SellsRepository) : ViewModel() {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "SellsViewModel"
|
||||
}
|
||||
|
||||
private val _sells = MutableLiveData<ViewState<List<OrdersItem?>?>>()
|
||||
val sells: LiveData<ViewState<List<OrdersItem?>?>> = _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
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
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.repository.ShippingServiceRepository
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ShippingServiceViewModel(private val repository: ShippingServiceRepository) : ViewModel() {
|
||||
|
||||
private val TAG = "ShippingServicesVM"
|
||||
|
||||
private val _availableCouriers = MutableLiveData<List<String>>()
|
||||
val availableCouriers: LiveData<List<String>> = _availableCouriers
|
||||
|
||||
private val _isLoading = MutableLiveData<Boolean>()
|
||||
val isLoading: LiveData<Boolean> = _isLoading
|
||||
|
||||
private val _errorMessage = MutableLiveData<String>()
|
||||
val errorMessage: LiveData<String> = _errorMessage
|
||||
|
||||
private val _saveSuccess = MutableLiveData<Boolean>()
|
||||
val saveSuccess: LiveData<Boolean> = _saveSuccess
|
||||
|
||||
fun getAvailableCouriers() {
|
||||
_isLoading.value = true
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val result = repository.getAvailableCouriers()
|
||||
_availableCouriers.value = result
|
||||
_isLoading.value = false
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error getting available couriers", e)
|
||||
_errorMessage.value = "Failed to load shipping services: ${e.message}"
|
||||
_isLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun saveShippingServices(selectedCouriers: List<String>) {
|
||||
if (selectedCouriers.isEmpty()) {
|
||||
_errorMessage.value = "Please select at least one courier"
|
||||
return
|
||||
}
|
||||
|
||||
_isLoading.value = true
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
// First get current couriers to determine what to add/delete
|
||||
val currentCouriers = repository.getAvailableCouriers()
|
||||
|
||||
// Calculate couriers to add (selected but not in current)
|
||||
val couriersToAdd = selectedCouriers.filter { !currentCouriers.contains(it) }
|
||||
|
||||
// Calculate couriers to delete (in current but not selected)
|
||||
val couriersToDelete = currentCouriers.filter { !selectedCouriers.contains(it) }
|
||||
|
||||
// Perform additions if needed
|
||||
if (couriersToAdd.isNotEmpty()) {
|
||||
repository.addShippingServices(couriersToAdd)
|
||||
}
|
||||
|
||||
// Perform deletions if needed
|
||||
if (couriersToDelete.isNotEmpty()) {
|
||||
repository.deleteShippingServices(couriersToDelete)
|
||||
}
|
||||
|
||||
_saveSuccess.value = true
|
||||
_isLoading.value = false
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error saving shipping services", e)
|
||||
_errorMessage.value = "Failed to save shipping services: ${e.message}"
|
||||
_isLoading.value = false
|
||||
_saveSuccess.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -82,44 +82,61 @@
|
||||
android:text="Riwayat Saldo"
|
||||
android:layout_marginTop="10dp"/>
|
||||
|
||||
<!-- Date Picker dengan Icon -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_text_field"
|
||||
android:layout_marginTop="10dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
android:layout_marginTop="10dp">
|
||||
|
||||
<!-- Icon Kalender -->
|
||||
<ImageView
|
||||
android:id="@+id/iconDatePicker"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:src="@drawable/ic_calendar"
|
||||
android:contentDescription="Pilih Tanggal" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/edt_tgl_transaksi"
|
||||
<!-- Date Picker dengan Icon -->
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:hint="Pilih tanggal di sini"
|
||||
android:padding="8dp"
|
||||
style="@style/body_small"
|
||||
android:background="@null"
|
||||
android:focusable="false"
|
||||
android:clickable="true" />
|
||||
android:background="@drawable/bg_text_field"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/img_date_picker"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:src="@drawable/ic_navigate_next"
|
||||
android:contentDescription="Pilih Tanggal"
|
||||
app:tint="@color/black_300" />
|
||||
<!-- Icon Kalender -->
|
||||
<ImageView
|
||||
android:id="@+id/iconDatePicker"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:src="@drawable/ic_calendar"
|
||||
android:contentDescription="Pilih Tanggal" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/edt_tgl_transaksi"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:hint="Pilih tanggal di sini"
|
||||
android:padding="8dp"
|
||||
style="@style/body_small"
|
||||
android:background="@null"
|
||||
android:focusable="false"
|
||||
android:clickable="true" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/img_date_picker"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:src="@drawable/ic_navigate_next"
|
||||
android:contentDescription="Pilih Tanggal"
|
||||
app:tint="@color/black_300" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Clear Filter Button -->
|
||||
<Button
|
||||
android:id="@+id/btn_clear_filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:text="Clear"
|
||||
android:layout_marginStart="8dp"
|
||||
style="@style/button.small.secondary.short"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@ -138,8 +155,25 @@
|
||||
android:scrollbars="vertical"
|
||||
tools:listitem="@layout/item_balance_transaction" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_empty_state"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Tidak ada riwayat transaksi"
|
||||
android:gravity="center"
|
||||
android:padding="24dp"
|
||||
style="@style/body_medium"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
@ -203,48 +203,6 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Nomor Rekening -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="24dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Nomor Rekening"
|
||||
style="@style/body_medium"
|
||||
android:layout_marginEnd="4dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="*"
|
||||
style="@style/body_medium"
|
||||
android:textColor="@color/red_required"
|
||||
android:layout_gravity="end"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/edt_no_rekening"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_text_field"
|
||||
android:hint="Isi nomor rekening Anda di sini"
|
||||
android:padding="8dp"
|
||||
style="@style/body_small"
|
||||
android:layout_marginTop="10dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Tanggal Transaksi -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
@ -322,7 +280,8 @@
|
||||
<Button
|
||||
android:id="@+id/btn_send"
|
||||
android:text="Kirim"
|
||||
style="@style/button.large.disabled.long"/>
|
||||
style="@style/button.large.disabled.long"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -284,7 +284,7 @@
|
||||
android:layout_marginBottom="8dp" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_payment_methods"
|
||||
android:id="@+id/rv_payment_info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:listitem="@layout/item_payment_method" />
|
||||
|
9
app/src/main/res/layout/activity_detail_order.xml
Normal file
9
app/src/main/res/layout/activity_detail_order.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ui.profile.mystore.sells.order.DetailOrderActivity">
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
9
app/src/main/res/layout/activity_detail_payment.xml
Normal file
9
app/src/main/res/layout/activity_detail_payment.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ui.profile.mystore.sells.payment.DetailPaymentActivity">
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
9
app/src/main/res/layout/activity_detail_shipment.xml
Normal file
9
app/src/main/res/layout/activity_detail_shipment.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ui.profile.mystore.sells.shipment.DetailShipmentActivity">
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -26,34 +26,33 @@
|
||||
android:paddingHorizontal="@dimen/horizontal_safe_area"
|
||||
android:layout_marginTop="19dp">
|
||||
|
||||
<!-- Nama Lokasi -->
|
||||
<!-- Error display -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="24dp">
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_error"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Nama Lokasi"
|
||||
android:textColor="@color/red_required"
|
||||
style="@style/body_medium"
|
||||
android:layout_marginEnd="4dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/edt_nama_lokasi"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_text_field_disabled"
|
||||
android:text="Alamat Toko"
|
||||
android:padding="8dp"
|
||||
style="@style/body_small"
|
||||
android:layout_marginTop="10dp"
|
||||
android:enabled="false"/>
|
||||
android:padding="12dp"
|
||||
android:background="#FFEEEE"
|
||||
android:visibility="gone"
|
||||
android:text="Error message goes here" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_retry"
|
||||
style="@style/button.small.active.short"
|
||||
android:text="Retry"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="8dp"
|
||||
android:visibility="gone"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Jalan -->
|
||||
<!-- Provinsi -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -63,43 +62,44 @@
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Jalan"
|
||||
android:text="Provinsi"
|
||||
style="@style/body_medium"
|
||||
android:layout_marginEnd="4dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/edt_street"
|
||||
<!-- Spinner Dropdown dengan Chevron -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:background="@drawable/bg_text_field"
|
||||
android:padding="8dp"
|
||||
style="@style/body_small"
|
||||
android:layout_marginTop="10dp"/>
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginTop="10dp">
|
||||
|
||||
</LinearLayout>
|
||||
<Spinner
|
||||
android:id="@+id/spinner_province"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:padding="8dp"
|
||||
style="@style/body_small"
|
||||
android:background="@null"/>
|
||||
|
||||
<!-- Kecamatan -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="24dp">
|
||||
<ProgressBar
|
||||
android:id="@+id/province_progress_bar"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Kecamatan"
|
||||
style="@style/body_medium"
|
||||
android:layout_marginEnd="4dp"/>
|
||||
<!-- Chevron Down Icon -->
|
||||
<ImageView
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:src="@drawable/ic_down"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:contentDescription="Chevron Down" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/edt_subdistrict"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_text_field"
|
||||
android:padding="8dp"
|
||||
style="@style/body_small"
|
||||
android:layout_marginTop="10dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@ -135,6 +135,13 @@
|
||||
style="@style/body_small"
|
||||
android:background="@null"/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/city_progress_bar"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<!-- Chevron Down Icon -->
|
||||
<ImageView
|
||||
android:layout_width="16dp"
|
||||
@ -147,7 +154,7 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Provinsi -->
|
||||
<!-- Jalan -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -157,37 +164,45 @@
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Provinsi"
|
||||
android:text="Jalan"
|
||||
style="@style/body_medium"
|
||||
android:layout_marginEnd="4dp"/>
|
||||
|
||||
<!-- Spinner Dropdown dengan Chevron -->
|
||||
<LinearLayout
|
||||
<EditText
|
||||
android:id="@+id/edt_street"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:background="@drawable/bg_text_field"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginTop="10dp">
|
||||
android:padding="8dp"
|
||||
style="@style/body_small"
|
||||
android:hint="Isi nama jalan di sini"
|
||||
android:layout_marginTop="10dp"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner_province"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:padding="8dp"
|
||||
style="@style/body_small"
|
||||
android:background="@null"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Chevron Down Icon -->
|
||||
<ImageView
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:src="@drawable/ic_down"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:contentDescription="Chevron Down" />
|
||||
<!-- Kecamatan -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="24dp">
|
||||
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Kecamatan"
|
||||
style="@style/body_medium"
|
||||
android:layout_marginEnd="4dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/edt_subdistrict"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_text_field"
|
||||
android:padding="8dp"
|
||||
style="@style/body_small"
|
||||
android:hint="Isi nama kecamatan di sini"
|
||||
android:layout_marginTop="10dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@ -212,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"/>
|
||||
|
||||
</LinearLayout>
|
||||
@ -245,38 +261,106 @@
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Pinpoint Lokasi -->
|
||||
<!-- <LinearLayout-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:orientation="vertical"-->
|
||||
<!-- android:layout_marginBottom="24dp">-->
|
||||
|
||||
<!-- <TextView-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:text="Pinpoint Lokasi"-->
|
||||
<!-- style="@style/body_medium"-->
|
||||
<!-- android:layout_marginEnd="4dp"/>-->
|
||||
|
||||
<!-- <!– Map –>-->
|
||||
<!-- <org.osmdroid.views.MapView android:id="@+id/map"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="220dp" />-->
|
||||
|
||||
<!-- <TextView-->
|
||||
<!-- android:id="@+id/tv_location_display"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:text="Lokasi: Tidak dipilih"-->
|
||||
<!-- style="@style/body_medium"/>-->
|
||||
|
||||
<!-- </LinearLayout>-->
|
||||
|
||||
<!-- Coordinates -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="24dp">
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
<!-- Latitude -->
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Pinpoint Lokasi"
|
||||
style="@style/body_medium"
|
||||
android:layout_marginEnd="4dp"/>
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginEnd="8dp">
|
||||
|
||||
<!-- Map -->
|
||||
<org.osmdroid.views.MapView android:id="@+id/map"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="220dp" />
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Latitude"
|
||||
style="@style/body_medium"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_location_display"
|
||||
android:layout_width="match_parent"
|
||||
<EditText
|
||||
android:id="@+id/edt_latitude"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_text_field"
|
||||
android:padding="12dp"
|
||||
android:hint="Latitude"
|
||||
android:inputType="numberDecimal|numberSigned"
|
||||
style="@style/body_small"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Longitude -->
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Lokasi: Tidak dipilih"
|
||||
style="@style/body_medium"/>
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginStart="8dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Longitude"
|
||||
style="@style/body_medium"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/edt_longitude"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_text_field"
|
||||
android:padding="12dp"
|
||||
android:hint="Longitude"
|
||||
android:inputType="numberDecimal|numberSigned"
|
||||
style="@style/body_small"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_save_address"
|
||||
android:text="Simpan Perubahan"
|
||||
style="@style/button.large.disabled.long"
|
||||
android:enabled="false"/>
|
||||
android:enabled="false"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -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"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -8,7 +8,9 @@
|
||||
android:orientation="vertical"
|
||||
tools:context=".ui.profile.mystore.profile.DetailStoreProfileActivity">
|
||||
|
||||
<include layout="@layout/header" />
|
||||
<include
|
||||
android:id="@+id/header"
|
||||
layout="@layout/header" />
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
|
183
app/src/main/res/layout/activity_edit_store_profile.xml
Normal file
183
app/src/main/res/layout/activity_edit_store_profile.xml
Normal file
@ -0,0 +1,183 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context=".ui.profile.mystore.profile.EditStoreProfileActivity">
|
||||
|
||||
<include
|
||||
android:id="@+id/header"
|
||||
layout="@layout/header" />
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingVertical="@dimen/vertical_safe_area"
|
||||
android:paddingHorizontal="@dimen/horizontal_safe_area">
|
||||
|
||||
<!-- Store Image -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Foto Toko"
|
||||
style="@style/body_medium"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_store_image"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="120dp"
|
||||
android:src="@drawable/placeholder_image"
|
||||
android:scaleType="centerCrop"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:background="@drawable/bg_text_field"/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_image"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
android:layout_marginTop="-80dp"
|
||||
android:layout_marginBottom="40dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_select_store_image"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Pilih Foto"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="8dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Store Name -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Nama Toko *"
|
||||
style="@style/body_medium"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/edt_store_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_text_field"
|
||||
android:padding="12dp"
|
||||
android:hint="Masukkan nama toko"
|
||||
android:inputType="text"
|
||||
style="@style/body_small"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Store Description -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Deskripsi Toko"
|
||||
style="@style/body_medium"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/edt_description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="120dp"
|
||||
android:background="@drawable/bg_text_field"
|
||||
android:padding="12dp"
|
||||
android:hint="Masukkan deskripsi toko"
|
||||
android:inputType="textMultiLine"
|
||||
android:gravity="top"
|
||||
style="@style/body_small"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Is On Leave -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Toko Sedang Cuti"
|
||||
style="@style/body_medium"/>
|
||||
|
||||
<Switch
|
||||
android:id="@+id/switch_is_on_leave"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- User Phone -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Nomor Telepon *"
|
||||
style="@style/body_medium"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/edt_user_phone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_text_field"
|
||||
android:padding="12dp"
|
||||
android:hint="Masukkan nomor telepon"
|
||||
android:inputType="phone"
|
||||
style="@style/body_small"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Save Button -->
|
||||
<Button
|
||||
android:id="@+id/btn_save"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Simpan"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- Progress Bar -->
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
@ -2,9 +2,82 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/white"
|
||||
tools:context=".ui.profile.mystore.profile.payment_info.PaymentInfoActivity">
|
||||
|
||||
<include
|
||||
android:id="@+id/header"
|
||||
layout="@layout/header"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/tv_empty_state"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/header">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:src="@drawable/placeholder_image"
|
||||
android:alpha="0.5" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Belum ada metode pembayaran"
|
||||
android:textAlignment="center"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="Tambahkan metode pembayaran untuk memudahkan pembeli melakukan transaksi"
|
||||
android:textAlignment="center"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_payment_info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:paddingHorizontal="@dimen/horizontal_safe_area"
|
||||
android:paddingVertical="8dp"
|
||||
android:clipToPadding="false"
|
||||
app:layout_constraintTop_toBottomOf="@id/header"
|
||||
app:layout_constraintBottom_toTopOf="@id/btn_add_payment"
|
||||
tools:listitem="@layout/item_payment_info"
|
||||
tools:itemCount="2" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/header" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_add_payment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:text="Tambahkan Metode Pembayaran"
|
||||
android:paddingVertical="12dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,14 +1,55 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context=".ui.profile.mystore.sells.SellsActivity">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/sells_fragment_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
<include
|
||||
android:id="@+id/header"
|
||||
layout="@layout/header" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
<!-- Search Bar -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp"
|
||||
android:background="@color/white">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:background="@drawable/bg_text_field"
|
||||
android:paddingHorizontal="6dp"
|
||||
android:paddingVertical="10dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="14dp"
|
||||
android:layout_height="14dp"
|
||||
android:src="@drawable/ic_search"
|
||||
android:contentDescription="Search Icon" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/edt_search"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@null"
|
||||
android:hint="Cari No. Pesanan, Nama Pemesan, Nama Produk, atau Lokasi di sini..."
|
||||
android:inputType="text"
|
||||
style="@style/body_small"
|
||||
android:layout_marginStart="6dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/fragment_container_sells"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp" />
|
||||
|
||||
</LinearLayout>
|
@ -2,10 +2,130 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:fitsSystemWindows="true"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/white"
|
||||
tools:context=".ui.profile.mystore.profile.shipping_service.ShippingServiceActivity">
|
||||
|
||||
<include
|
||||
android:id="@+id/header"
|
||||
layout="@layout/header"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/content_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/header"
|
||||
app:layout_constraintBottom_toTopOf="@id/btn_save">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Pilih Layanan Pengiriman"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="16dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Layanan pengiriman yang dipilih akan tersedia untuk pembeli saat checkout"
|
||||
android:textSize="14sp"
|
||||
android:layout_marginBottom="24dp" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkbox_jne"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="JNE"
|
||||
android:textSize="16sp"
|
||||
android:paddingStart="8dp" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkbox_pos"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="POS Indonesia"
|
||||
android:textSize="16sp"
|
||||
android:paddingStart="8dp" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkbox_tiki"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="TIKI"
|
||||
android:textSize="16sp"
|
||||
android:paddingStart="8dp" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkbox_sicepat"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="SiCepat"
|
||||
android:textSize="16sp"
|
||||
android:paddingStart="8dp" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkbox_jnt"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="J&T Express"
|
||||
android:textSize="16sp"
|
||||
android:paddingStart="8dp" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkbox_ninja"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Ninja Express"
|
||||
android:textSize="16sp"
|
||||
android:paddingStart="8dp" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkbox_antaraja"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="AnterAja"
|
||||
android:textSize="16sp"
|
||||
android:paddingStart="8dp" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkbox_spx"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Shopee Express (SPX)"
|
||||
android:textSize="16sp"
|
||||
android:paddingStart="8dp" />
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/header" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_save"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:text="Simpan"
|
||||
android:paddingVertical="12dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
102
app/src/main/res/layout/dialog_add_payment_info.xml
Normal file
102
app/src/main/res/layout/dialog_add_payment_info.xml
Normal file
@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Tambah Metode Pembayaran"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="16dp" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/edt_bank_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Nama Bank" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/edt_account_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Nama Pemilik Rekening" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/edt_bank_number"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Nomor Rekening"
|
||||
android:inputType="number" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="QRIS (Opsional)"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="8dp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_add_qris"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Tambah Gambar QRIS"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_marginBottom="8dp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_qris_preview"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="150dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:visibility="gone"
|
||||
android:contentDescription="QRIS Preview" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="end">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_cancel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Batal"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
android:layout_marginEnd="8dp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_save"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Simpan" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/rv_all_sells"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/rv_cancellation"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/rv_completed"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/rv_failed_payment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/rv_failed_shipment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/rv_order"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:listitem="@layout/item_sells_order" />
|
@ -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" />
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/viewPager"
|
||||
|
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/rv_payment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:listitem="@layout/item_sells_payment"/>
|
38
app/src/main/res/layout/fragment_sells_list.xml
Normal file
38
app/src/main/res/layout/fragment_sells_list.xml
Normal file
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ui.profile.mystore.sells.SellsListFragment">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_sells"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_sells_order" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_empty_state"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="TIdak ada penjualan"
|
||||
android:textSize="16sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</FrameLayout>
|
@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/rv_shipment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:listitem="@layout/item_sells_shipment" />
|
@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/rv_shipped"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
92
app/src/main/res/layout/item_payment_info.xml
Normal file
92
app/src/main/res/layout/item_payment_info.xml
Normal file
@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:cardElevation="2dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_info"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/iv_delete"
|
||||
app:layout_constraintBottom_toTopOf="@id/layout_qris">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_bank_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="Mandiri" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_account_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:layout_marginTop="4dp"
|
||||
tools:text="Kemas" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_bank_number"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:layout_marginTop="4dp"
|
||||
tools:text="941281212313" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_delete"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/ic_delete"
|
||||
android:contentDescription="Delete payment method"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_qris"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginTop="16dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toBottomOf="@id/layout_info"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="QRIS"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_qris"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="120dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:contentDescription="QRIS" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
@ -16,7 +16,7 @@
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_order_header"
|
||||
android:id="@+id/layout_sells_header"
|
||||
android:layout_width="220dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
@ -25,7 +25,7 @@
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_order_title"
|
||||
android:id="@+id/tv_sells_title"
|
||||
style="@style/label_medium_prominent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -33,7 +33,7 @@
|
||||
android:text="Pesanan Perlu Dibuat Tagihan"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_order_number"
|
||||
android:id="@+id/tv_sells_number"
|
||||
style="@style/label_small"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -52,7 +52,7 @@
|
||||
android:src="@drawable/ic_person"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_order_customer"
|
||||
android:id="@+id/tv_sells_customer"
|
||||
style="@style/label_small"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -84,7 +84,7 @@
|
||||
android:textColor="@color/black_300" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_order_due"
|
||||
android:id="@+id/tv_sells_due"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="25 Okt; 23.59"
|
||||
@ -97,11 +97,11 @@
|
||||
|
||||
<!-- Order Detail -->
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_order_detail"
|
||||
android:id="@+id/layout_sells_detail"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/layout_order_header"
|
||||
app:layout_constraintTop_toBottomOf="@id/layout_sells_header"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:orientation="vertical">
|
||||
@ -113,13 +113,13 @@
|
||||
|
||||
<!-- Product Detail -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/layout_order_product_detail"
|
||||
android:id="@+id/layout_sells_product_detail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingVertical="12dp">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/iv_order_product"
|
||||
android:id="@+id/iv_sells_product"
|
||||
android:layout_width="95dp"
|
||||
android:layout_height="64dp"
|
||||
android:src="@drawable/placeholder_image"
|
||||
@ -134,26 +134,26 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginStart="13dp"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_order_product"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_sells_product"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_order_product_name"
|
||||
android:id="@+id/tv_sells_product_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:text="Jaket Pink Fuschia"
|
||||
style="@style/label_medium_prominent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_order_product_variant"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:text="S"
|
||||
style="@style/label_medium"
|
||||
android:textColor="@color/black_300"/>
|
||||
<!-- <TextView-->
|
||||
<!-- android:id="@+id/tv_sells_product_variant"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:maxLines="1"-->
|
||||
<!-- android:text="S"-->
|
||||
<!-- style="@style/label_medium"-->
|
||||
<!-- android:textColor="@color/black_300"/>-->
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@ -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">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_order_product_qty"
|
||||
android:id="@+id/tv_sells_product_qty"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
@ -178,7 +178,7 @@
|
||||
android:textAlignment="textEnd"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_order_product_price"
|
||||
android:id="@+id/tv_sells_product_price"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
@ -198,7 +198,7 @@
|
||||
style="@style/label_small"
|
||||
android:fontFamily="@font/dmsans_italic"
|
||||
android:textColor="@color/black_300"
|
||||
app:layout_constraintTop_toBottomOf="@id/iv_order_product"
|
||||
app:layout_constraintTop_toBottomOf="@id/iv_sells_product"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_marginVertical="8dp"
|
||||
@ -219,7 +219,7 @@
|
||||
android:paddingVertical="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_order_qty"
|
||||
android:id="@+id/tv_sells_qty"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="2 produk"
|
||||
@ -240,7 +240,7 @@
|
||||
android:textAlignment="textEnd"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_order_price"
|
||||
android:id="@+id/tv_sells_price"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Rp300.000"
|
||||
@ -287,6 +287,6 @@
|
||||
android:background="@color/black_50"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/layout_order_detail"/>
|
||||
app:layout_constraintTop_toBottomOf="@id/layout_sells_detail"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -16,7 +16,7 @@
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_payment_header"
|
||||
android:id="@+id/layout_sells_header"
|
||||
android:layout_width="220dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
@ -25,7 +25,7 @@
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_payment_title"
|
||||
android:id="@+id/tv_sells_title"
|
||||
style="@style/label_medium_prominent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -33,7 +33,7 @@
|
||||
android:text="Pesanan Telah Dibayar"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_payment_number"
|
||||
android:id="@+id/tv_sells_number"
|
||||
style="@style/label_small"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -53,6 +53,7 @@
|
||||
android:gravity="end">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_sells_due_desc"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
@ -62,7 +63,7 @@
|
||||
android:textColor="@color/black_300" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_payment_due"
|
||||
android:id="@+id/tv_sells_due"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="25 Okt; 23.59"
|
||||
@ -75,11 +76,11 @@
|
||||
|
||||
<!-- Order Detail -->
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_payment_detail"
|
||||
android:id="@+id/layout_sells_detail"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/layout_payment_header"
|
||||
app:layout_constraintTop_toBottomOf="@id/layout_sells_header"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:orientation="vertical">
|
||||
@ -91,13 +92,13 @@
|
||||
|
||||
<!-- Product Detail -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/layout_payment_product_detail"
|
||||
android:id="@+id/layout_sells_product_detail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingVertical="12dp">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/iv_payment_product"
|
||||
android:id="@+id/iv_sells_product"
|
||||
android:layout_width="95dp"
|
||||
android:layout_height="64dp"
|
||||
android:src="@drawable/placeholder_image"
|
||||
@ -112,26 +113,26 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginStart="13dp"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_payment_product"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_sells_product"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_payment_product_name"
|
||||
android:id="@+id/tv_sells_product_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:text="Jaket Pink Fuschia"
|
||||
style="@style/label_medium_prominent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_payment_product_variant"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:text="S"
|
||||
style="@style/label_medium"
|
||||
android:textColor="@color/black_300"/>
|
||||
<!-- <TextView-->
|
||||
<!-- android:id="@+id/tv_sells_product_variant"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:maxLines="1"-->
|
||||
<!-- android:text="S"-->
|
||||
<!-- style="@style/label_medium"-->
|
||||
<!-- android:textColor="@color/black_300"/>-->
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@ -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">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_payment_product_qty"
|
||||
android:id="@+id/tv_sells_product_qty"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
@ -156,7 +157,7 @@
|
||||
android:textAlignment="textEnd"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_payment_product_price"
|
||||
android:id="@+id/tv_sells_product_price"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
@ -176,7 +177,7 @@
|
||||
style="@style/label_small"
|
||||
android:fontFamily="@font/dmsans_italic"
|
||||
android:textColor="@color/black_300"
|
||||
app:layout_constraintTop_toBottomOf="@id/iv_payment_product"
|
||||
app:layout_constraintTop_toBottomOf="@id/iv_sells_product"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_marginVertical="8dp"
|
||||
@ -197,7 +198,7 @@
|
||||
android:paddingVertical="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_payment_qty"
|
||||
android:id="@+id/tv_sells_qty"
|
||||
style="@style/label_large"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@ -218,7 +219,7 @@
|
||||
android:textAlignment="textEnd"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_payment_price"
|
||||
android:id="@+id/tv_sells_price"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Rp300.000"
|
||||
@ -262,7 +263,7 @@
|
||||
app:tint="@color/black_500" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_payment_customer"
|
||||
android:id="@+id/tv_sells_customer"
|
||||
style="@style/label_small"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -284,7 +285,7 @@
|
||||
android:src="@drawable/ic_location" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_payment_location"
|
||||
android:id="@+id/tv_sells_location"
|
||||
style="@style/label_small"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -313,6 +314,6 @@
|
||||
android:background="@color/black_50"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/layout_payment_detail"/>
|
||||
app:layout_constraintTop_toBottomOf="@id/layout_sells_detail"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -16,7 +16,7 @@
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_shipment_header"
|
||||
android:id="@+id/layout_sells_header"
|
||||
android:layout_width="220dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
@ -25,7 +25,7 @@
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_shipment_title"
|
||||
android:id="@+id/tv_sells_title"
|
||||
style="@style/label_medium_prominent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -33,7 +33,7 @@
|
||||
android:text="Pesanan Perlu Dikirim"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_shipment_number"
|
||||
android:id="@+id/tv_sells_number"
|
||||
style="@style/label_small"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -62,7 +62,7 @@
|
||||
android:textColor="@color/black_300" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_shipment_due"
|
||||
android:id="@+id/tv_sells_due"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="25 Okt; 23.59"
|
||||
@ -75,11 +75,11 @@
|
||||
|
||||
<!-- Order Detail -->
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_shipment_detail"
|
||||
android:id="@+id/layout_sells_detail"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/layout_shipment_header"
|
||||
app:layout_constraintTop_toBottomOf="@id/layout_sells_header"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:orientation="vertical">
|
||||
@ -91,13 +91,13 @@
|
||||
|
||||
<!-- Product Detail -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/layout_shipment_product_detail"
|
||||
android:id="@+id/layout_sells_product_detail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingVertical="12dp">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/iv_shipment_product"
|
||||
android:id="@+id/iv_sells_product"
|
||||
android:layout_width="95dp"
|
||||
android:layout_height="48dp"
|
||||
android:src="@drawable/placeholder_image"
|
||||
@ -112,31 +112,31 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginStart="13dp"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_shipment_product"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_sells_product"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_shipment_product_name"
|
||||
android:id="@+id/tv_sells_product_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:text="Jaket Pink Fuschia"
|
||||
style="@style/label_medium_prominent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_shipment_product_variant"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:text="S"
|
||||
style="@style/label_medium"
|
||||
android:textColor="@color/black_300"/>
|
||||
<!-- <TextView-->
|
||||
<!-- android:id="@+id/tv_sells_product_variant"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:maxLines="1"-->
|
||||
<!-- android:text="S"-->
|
||||
<!-- style="@style/label_medium"-->
|
||||
<!-- android:textColor="@color/black_300"/>-->
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_shipment_product_qty"
|
||||
android:id="@+id/tv_sells_product_qty"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
@ -145,9 +145,9 @@
|
||||
android:layout_marginStart="13dp"
|
||||
android:gravity="end"
|
||||
android:textAlignment="textEnd"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_shipment_product"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_sells_product"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="@id/iv_shipment_product"/>
|
||||
app:layout_constraintBottom_toBottomOf="@id/iv_sells_product"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_see_more"
|
||||
@ -159,7 +159,7 @@
|
||||
style="@style/label_small"
|
||||
android:fontFamily="@font/dmsans_italic"
|
||||
android:textColor="@color/black_300"
|
||||
app:layout_constraintTop_toBottomOf="@id/iv_shipment_product"
|
||||
app:layout_constraintTop_toBottomOf="@id/iv_sells_product"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_marginVertical="8dp"
|
||||
@ -199,7 +199,7 @@
|
||||
app:tint="@color/black_500" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_shipment_customer"
|
||||
android:id="@+id/tv_sells_customer"
|
||||
style="@style/label_small"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -221,7 +221,7 @@
|
||||
android:src="@drawable/ic_location" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_shipment_location"
|
||||
android:id="@+id/tv_sells_location"
|
||||
style="@style/label_small"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -250,6 +250,6 @@
|
||||
android:background="@color/black_50"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/layout_shipment_detail"/>
|
||||
app:layout_constraintTop_toBottomOf="@id/layout_sells_detail"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -10,7 +10,7 @@
|
||||
|
||||
<string name="item_price_txt">Rp%.1f</string>
|
||||
<string name="retry">Coba lagi\n</string>
|
||||
<string name="error_loading">Terdapat error...</string>
|
||||
<string name="error_loading">Terdapat error…</string>
|
||||
<string name="new_products_text">Produk Terbaru</string>
|
||||
<string name="rating">4.5</string>
|
||||
<string name="open_store">Buka Toko</string>
|
||||
|
@ -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"
|
||||
|
Reference in New Issue
Block a user