mirror of
https://github.com/shaulascr/ecommerce_serang.git
synced 2025-12-16 07:51:02 +00:00
fix register store and product
This commit is contained in:
@ -0,0 +1,21 @@
|
|||||||
|
package com.alya.ecommerce_serang.data.api.dto
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
data class PaymentUpdate(
|
||||||
|
@field:SerializedName("id")
|
||||||
|
val id: Int? = null,
|
||||||
|
|
||||||
|
@field:SerializedName("bank_name")
|
||||||
|
val bankName: String,
|
||||||
|
|
||||||
|
@field:SerializedName("bank_num")
|
||||||
|
val bankNum: String,
|
||||||
|
|
||||||
|
@field:SerializedName("account_name")
|
||||||
|
val accountName: String,
|
||||||
|
|
||||||
|
@field:SerializedName("qris_image")
|
||||||
|
val qrisImage: File? = null
|
||||||
|
)
|
||||||
@ -1,7 +1,6 @@
|
|||||||
package com.alya.ecommerce_serang.data.api.retrofit
|
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.AddPaymentInfoResponse
|
||||||
import com.alya.ecommerce_serang.data.api.dto.CancelOrderReq
|
import com.alya.ecommerce_serang.data.api.dto.CancelOrderReq
|
||||||
import com.alya.ecommerce_serang.data.api.dto.CartItem
|
import com.alya.ecommerce_serang.data.api.dto.CartItem
|
||||||
@ -17,7 +16,6 @@ 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.OrderRequest
|
||||||
import com.alya.ecommerce_serang.data.api.dto.OrderRequestBuy
|
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.OtpRequest
|
||||||
import com.alya.ecommerce_serang.data.api.dto.PaymentConfirmRequest
|
|
||||||
import com.alya.ecommerce_serang.data.api.dto.ProvinceResponse
|
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.RegisterRequest
|
||||||
import com.alya.ecommerce_serang.data.api.dto.ResetPassReq
|
import com.alya.ecommerce_serang.data.api.dto.ResetPassReq
|
||||||
@ -29,7 +27,6 @@ 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.dto.UpdateChatRequest
|
||||||
import com.alya.ecommerce_serang.data.api.dto.VerifRegisReq
|
import com.alya.ecommerce_serang.data.api.dto.VerifRegisReq
|
||||||
import com.alya.ecommerce_serang.data.api.response.auth.ChangePassResponse
|
import com.alya.ecommerce_serang.data.api.response.auth.ChangePassResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.auth.CheckStoreResponse
|
|
||||||
import com.alya.ecommerce_serang.data.api.response.auth.FcmTokenResponse
|
import com.alya.ecommerce_serang.data.api.response.auth.FcmTokenResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.auth.HasStoreResponse
|
import com.alya.ecommerce_serang.data.api.response.auth.HasStoreResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.auth.ListNotifResponse
|
import com.alya.ecommerce_serang.data.api.response.auth.ListNotifResponse
|
||||||
@ -83,12 +80,10 @@ import com.alya.ecommerce_serang.data.api.response.store.product.UpdateProductRe
|
|||||||
import com.alya.ecommerce_serang.data.api.response.store.product.ViewStoreProductsResponse
|
import com.alya.ecommerce_serang.data.api.response.store.product.ViewStoreProductsResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.store.profile.StoreDataResponse
|
import com.alya.ecommerce_serang.data.api.response.store.profile.StoreDataResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.store.review.ProductReviewResponse
|
import com.alya.ecommerce_serang.data.api.response.store.review.ProductReviewResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.store.sells.PaymentConfirmationResponse
|
|
||||||
import com.alya.ecommerce_serang.data.api.response.store.topup.BalanceTopUpResponse
|
import com.alya.ecommerce_serang.data.api.response.store.topup.BalanceTopUpResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.store.topup.TopUpResponse
|
import com.alya.ecommerce_serang.data.api.response.store.topup.TopUpResponse
|
||||||
import okhttp3.MultipartBody
|
import okhttp3.MultipartBody
|
||||||
import okhttp3.RequestBody
|
import okhttp3.RequestBody
|
||||||
import retrofit2.Call
|
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import retrofit2.http.Body
|
import retrofit2.http.Body
|
||||||
import retrofit2.http.DELETE
|
import retrofit2.http.DELETE
|
||||||
@ -99,7 +94,6 @@ import retrofit2.http.PUT
|
|||||||
import retrofit2.http.Part
|
import retrofit2.http.Part
|
||||||
import retrofit2.http.PartMap
|
import retrofit2.http.PartMap
|
||||||
import retrofit2.http.Path
|
import retrofit2.http.Path
|
||||||
import retrofit2.http.Query
|
|
||||||
|
|
||||||
interface ApiService {
|
interface ApiService {
|
||||||
@POST("registeruser")
|
@POST("registeruser")
|
||||||
@ -112,9 +106,6 @@ interface ApiService {
|
|||||||
@Body verifRegisReq: VerifRegisReq
|
@Body verifRegisReq: VerifRegisReq
|
||||||
):VerifRegisterResponse
|
):VerifRegisterResponse
|
||||||
|
|
||||||
@GET("checkstore")
|
|
||||||
suspend fun checkStore (): Response<CheckStoreResponse>
|
|
||||||
|
|
||||||
@Multipart
|
@Multipart
|
||||||
@POST("registerstore")
|
@POST("registerstore")
|
||||||
suspend fun registerStore(
|
suspend fun registerStore(
|
||||||
@ -204,11 +195,6 @@ interface ApiService {
|
|||||||
@Path("id") orderId: Int
|
@Path("id") orderId: Int
|
||||||
): Response<OrderDetailResponse>
|
): Response<OrderDetailResponse>
|
||||||
|
|
||||||
@POST("order/addevidence")
|
|
||||||
suspend fun addEvidence(
|
|
||||||
@Body request : AddEvidenceRequest,
|
|
||||||
): Response<AddEvidenceResponse>
|
|
||||||
|
|
||||||
@Multipart
|
@Multipart
|
||||||
@POST("order/addevidence")
|
@POST("order/addevidence")
|
||||||
suspend fun addEvidenceMultipart(
|
suspend fun addEvidenceMultipart(
|
||||||
@ -256,15 +242,9 @@ interface ApiService {
|
|||||||
@GET("mystore")
|
@GET("mystore")
|
||||||
suspend fun getMyStoreData(): Response<com.alya.ecommerce_serang.data.api.response.store.StoreResponse>
|
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
|
@GET("mystore/product") // Replace with actual endpoint
|
||||||
suspend fun getStoreProduct(): Response<ViewStoreProductsResponse>
|
suspend fun getStoreProduct(): Response<ViewStoreProductsResponse>
|
||||||
|
|
||||||
@GET("category")
|
|
||||||
fun getCategories(): Call<CategoryResponse>
|
|
||||||
|
|
||||||
@Multipart
|
@Multipart
|
||||||
@POST("store/createproduct")
|
@POST("store/createproduct")
|
||||||
suspend fun addProduct(
|
suspend fun addProduct(
|
||||||
@ -374,9 +354,6 @@ interface ApiService {
|
|||||||
@GET("store/topup")
|
@GET("store/topup")
|
||||||
suspend fun getTopUpHistory(): Response<TopUpResponse>
|
suspend fun getTopUpHistory(): Response<TopUpResponse>
|
||||||
|
|
||||||
@GET("store/topup")
|
|
||||||
suspend fun getFilteredTopUpHistory(@Query("date") date: String): Response<TopUpResponse>
|
|
||||||
|
|
||||||
@Multipart
|
@Multipart
|
||||||
@POST("store/createtopup")
|
@POST("store/createtopup")
|
||||||
suspend fun addBalanceTopUp(
|
suspend fun addBalanceTopUp(
|
||||||
@ -388,11 +365,6 @@ interface ApiService {
|
|||||||
@Part("bank_num") bankNum: RequestBody
|
@Part("bank_num") bankNum: RequestBody
|
||||||
): Response<BalanceTopUpResponse>
|
): Response<BalanceTopUpResponse>
|
||||||
|
|
||||||
@PUT("store/payment/update")
|
|
||||||
suspend fun paymentConfirmation(
|
|
||||||
@Body confirmPaymentReq : PaymentConfirmRequest
|
|
||||||
): Response<PaymentConfirmationResponse>
|
|
||||||
|
|
||||||
@Multipart
|
@Multipart
|
||||||
@PUT("mystore/edit")
|
@PUT("mystore/edit")
|
||||||
suspend fun updateStoreProfileMultipart(
|
suspend fun updateStoreProfileMultipart(
|
||||||
@ -404,13 +376,26 @@ interface ApiService {
|
|||||||
): Response<StoreDataResponse>
|
): Response<StoreDataResponse>
|
||||||
|
|
||||||
@Multipart
|
@Multipart
|
||||||
@POST("mystore/payment/add")
|
@PUT("mystore/edit")
|
||||||
suspend fun addPaymentInfo(
|
suspend fun updateStoreApprovalMultipart(
|
||||||
@Part("bank_name") bankName: RequestBody,
|
@Part("store_name") storeName: RequestBody,
|
||||||
@Part("bank_num") bankNum: RequestBody,
|
@Part("store_description") storeDescription: RequestBody,
|
||||||
@Part("account_name") accountName: RequestBody,
|
@Part("store_type_id") storeTypeId: RequestBody,
|
||||||
@Part qris: MultipartBody.Part?
|
@Part("latitude") storeLatitude: RequestBody,
|
||||||
): Response<GenericResponse>
|
@Part("longitude") storeLongitude: RequestBody,
|
||||||
|
@Part("province_id") storeProvince: RequestBody,
|
||||||
|
@Part("city_id") storeCity: RequestBody,
|
||||||
|
@Part("subdistrict") storeSubdistrict: RequestBody,
|
||||||
|
@Part("village_id") storeVillage: RequestBody,
|
||||||
|
@Part("street") storeStreet: RequestBody,
|
||||||
|
@Part("postal_code") storePostalCode: RequestBody,
|
||||||
|
@Part("detail") storeAddressDetail: RequestBody,
|
||||||
|
@Part("user_phone") storeUserPhone: RequestBody,
|
||||||
|
@Part storeimg: MultipartBody.Part?,
|
||||||
|
@Part ktp: MultipartBody.Part?,
|
||||||
|
@Part npwp: MultipartBody.Part?,
|
||||||
|
@Part nib: MultipartBody.Part?
|
||||||
|
): Response<StoreDataResponse>
|
||||||
|
|
||||||
@Multipart
|
@Multipart
|
||||||
@POST("mystore/payment/add")
|
@POST("mystore/payment/add")
|
||||||
@ -421,6 +406,16 @@ interface ApiService {
|
|||||||
@Part qris: MultipartBody.Part?
|
@Part qris: MultipartBody.Part?
|
||||||
): Response<AddPaymentInfoResponse>
|
): Response<AddPaymentInfoResponse>
|
||||||
|
|
||||||
|
@Multipart
|
||||||
|
@PUT("mystore/payment/edit")
|
||||||
|
suspend fun updatePaymentInfo(
|
||||||
|
@Part("payment_info_id") paymentInfoId: RequestBody,
|
||||||
|
@Part("account_name") accountName: RequestBody,
|
||||||
|
@Part("bank_name") bankName: RequestBody,
|
||||||
|
@Part("bank_num") bankNum: RequestBody,
|
||||||
|
@Part qris: MultipartBody.Part? = null
|
||||||
|
): Response<GenericResponse>
|
||||||
|
|
||||||
@DELETE("mystore/payment/delete/{id}")
|
@DELETE("mystore/payment/delete/{id}")
|
||||||
suspend fun deletePaymentInfo(
|
suspend fun deletePaymentInfo(
|
||||||
@Path("id") paymentMethodId: Int
|
@Path("id") paymentMethodId: Int
|
||||||
@ -464,15 +459,6 @@ interface ApiService {
|
|||||||
@GET("search")
|
@GET("search")
|
||||||
suspend fun getSearchHistory(): Response<SearchHistoryResponse>
|
suspend fun getSearchHistory(): Response<SearchHistoryResponse>
|
||||||
|
|
||||||
@Multipart
|
|
||||||
@POST("sendchat")
|
|
||||||
suspend fun sendChatLine(
|
|
||||||
@Part("store_id") storeId: RequestBody,
|
|
||||||
@Part("message") message: RequestBody,
|
|
||||||
@Part("product_id") productId: RequestBody?,
|
|
||||||
@Part chatimg: MultipartBody.Part?
|
|
||||||
): Response<SendChatResponse>
|
|
||||||
|
|
||||||
@Multipart
|
@Multipart
|
||||||
@POST("store/sendchat")
|
@POST("store/sendchat")
|
||||||
suspend fun sendChatMessageStore(
|
suspend fun sendChatMessageStore(
|
||||||
|
|||||||
@ -1,17 +1,23 @@
|
|||||||
package com.alya.ecommerce_serang.data.repository
|
package com.alya.ecommerce_serang.data.repository
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import com.alya.ecommerce_serang.data.api.dto.PaymentUpdate
|
||||||
import com.alya.ecommerce_serang.data.api.dto.ProductsItem
|
import com.alya.ecommerce_serang.data.api.dto.ProductsItem
|
||||||
|
import com.alya.ecommerce_serang.data.api.dto.ShippingServiceRequest
|
||||||
import com.alya.ecommerce_serang.data.api.dto.Store
|
import com.alya.ecommerce_serang.data.api.dto.Store
|
||||||
import com.alya.ecommerce_serang.data.api.response.auth.ListStoreTypeResponse
|
import com.alya.ecommerce_serang.data.api.response.auth.ListStoreTypeResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.store.StoreResponse
|
import com.alya.ecommerce_serang.data.api.response.store.StoreResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.store.profile.StoreDataResponse
|
import com.alya.ecommerce_serang.data.api.response.store.profile.StoreDataResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.store.sells.OrderListResponse
|
import com.alya.ecommerce_serang.data.api.response.store.sells.OrderListResponse
|
||||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiService
|
import com.alya.ecommerce_serang.data.api.retrofit.ApiService
|
||||||
|
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||||
import okhttp3.MultipartBody
|
import okhttp3.MultipartBody
|
||||||
import okhttp3.RequestBody
|
import okhttp3.RequestBody
|
||||||
|
import okhttp3.RequestBody.Companion.asRequestBody
|
||||||
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class MyStoreRepository(private val apiService: ApiService) {
|
class MyStoreRepository(private val apiService: ApiService) {
|
||||||
@ -139,6 +145,186 @@ class MyStoreRepository(private val apiService: ApiService) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun updateStoreApproval(
|
||||||
|
storeName: RequestBody,
|
||||||
|
description: RequestBody,
|
||||||
|
storeType: RequestBody,
|
||||||
|
latitude: RequestBody,
|
||||||
|
longitude: RequestBody,
|
||||||
|
storeProvince: RequestBody,
|
||||||
|
storeCity: RequestBody,
|
||||||
|
storeSubdistrict: RequestBody,
|
||||||
|
storeVillage: RequestBody,
|
||||||
|
storeStreet: RequestBody,
|
||||||
|
storePostalCode: RequestBody,
|
||||||
|
storeAddressDetail: RequestBody,
|
||||||
|
userPhone: RequestBody,
|
||||||
|
paymentsToUpdate: List<PaymentUpdate> = emptyList(),
|
||||||
|
paymentIdToDelete: List<Int> = emptyList(),
|
||||||
|
storeCourier: List<String>? = null,
|
||||||
|
storeImage: MultipartBody.Part?,
|
||||||
|
ktpImage: MultipartBody.Part?,
|
||||||
|
npwpDocument: MultipartBody.Part?,
|
||||||
|
nibDocument: MultipartBody.Part?
|
||||||
|
): Response<StoreDataResponse>? {
|
||||||
|
return try {
|
||||||
|
Log.d(TAG, "Updating store profile & address for approval...")
|
||||||
|
|
||||||
|
val profileResp = apiService.updateStoreApprovalMultipart(
|
||||||
|
storeName = storeName,
|
||||||
|
storeDescription = description,
|
||||||
|
storeTypeId = storeType,
|
||||||
|
storeLatitude = latitude,
|
||||||
|
storeLongitude = longitude,
|
||||||
|
storeProvince = storeProvince,
|
||||||
|
storeCity = storeCity,
|
||||||
|
storeSubdistrict = storeSubdistrict,
|
||||||
|
storeVillage = storeVillage,
|
||||||
|
storeStreet = storeStreet,
|
||||||
|
storePostalCode = storePostalCode,
|
||||||
|
storeAddressDetail = storeAddressDetail,
|
||||||
|
storeUserPhone = userPhone,
|
||||||
|
storeimg = storeImage,
|
||||||
|
ktp = ktpImage,
|
||||||
|
npwp = npwpDocument,
|
||||||
|
nib = nibDocument
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!profileResp.isSuccessful) {
|
||||||
|
Log.e(TAG, "Profile update failed: ${profileResp.code()} ${profileResp.errorBody()?.string()}")
|
||||||
|
return profileResp // short-circuit; let caller inspect the failure
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Payments: delete, then upsert (safer if you’re changing accounts)
|
||||||
|
if (paymentIdToDelete.isNotEmpty() || paymentsToUpdate.isNotEmpty()) {
|
||||||
|
Log.d(TAG, "Synchronizing payments: delete=${paymentIdToDelete.size}, upsert=${paymentsToUpdate.size}")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2a) Delete payments
|
||||||
|
paymentIdToDelete.forEach { id ->
|
||||||
|
runCatching {
|
||||||
|
apiService.deletePaymentInfo(id)
|
||||||
|
}.onSuccess {
|
||||||
|
if (!it.isSuccessful) {
|
||||||
|
Log.e(TAG, "Delete payment $id failed: ${it.code()} ${it.errorBody()?.string()}")
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "Deleted payment $id")
|
||||||
|
}
|
||||||
|
}.onFailure { e ->
|
||||||
|
Log.e(TAG, "Delete payment $id exception", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2b) Upsert payments (add if id==null, else update)
|
||||||
|
paymentsToUpdate.forEach { item ->
|
||||||
|
runCatching {
|
||||||
|
// --- CHANGE HERE if your PaymentUpdate field names differ ---
|
||||||
|
val id = item.id // Int? (null => add)
|
||||||
|
val bankName = item.bankName // String
|
||||||
|
val bankNum = item.bankNum // String
|
||||||
|
val accountName = item.accountName // String
|
||||||
|
val qrisImage = item.qrisImage // File? (Optional)
|
||||||
|
// -----------------------------------------------------------
|
||||||
|
|
||||||
|
if (id == null) {
|
||||||
|
// ADD
|
||||||
|
val resp = apiService.addPaymentInfoDirect(
|
||||||
|
bankName = bankName.toPlain(),
|
||||||
|
bankNum = bankNum.toPlain(),
|
||||||
|
accountName = accountName.toPlain(),
|
||||||
|
qris = createQrisPartOrNull(qrisImage)
|
||||||
|
)
|
||||||
|
if (!resp.isSuccessful) {
|
||||||
|
Log.e(TAG, "Add payment failed: ${resp.code()} ${resp.errorBody()?.string()}")
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "Added payment: $bankName/$bankNum")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// UPDATE
|
||||||
|
val resp = apiService.updatePaymentInfo(
|
||||||
|
paymentInfoId = id.toString().toPlain(),
|
||||||
|
accountName = accountName.toPlain(),
|
||||||
|
bankName = bankName.toPlain(),
|
||||||
|
bankNum = bankNum.toPlain(),
|
||||||
|
qris = createQrisPartOrNull(qrisImage)
|
||||||
|
)
|
||||||
|
if (!resp.isSuccessful) {
|
||||||
|
Log.e(TAG, "Update payment $id failed: ${resp.code()} ${resp.errorBody()?.string()}")
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "Updated payment $id: $bankName/$bankNum")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.onFailure { e ->
|
||||||
|
Log.e(TAG, "Upsert payment exception", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) Shipping: sync to desiredCouriers (if provided)
|
||||||
|
storeCourier?.let { desired ->
|
||||||
|
try {
|
||||||
|
val current = apiService.getStoreData().let { resp ->
|
||||||
|
if (resp.isSuccessful) {
|
||||||
|
resp.body()?.shipping?.mapNotNull { it.courier } ?: emptyList()
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Failed to read current shipping: ${resp.code()} ${resp.errorBody()?.string()}")
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val desiredSet = desired.toSet()
|
||||||
|
val currentSet = current.toSet()
|
||||||
|
|
||||||
|
val toAdd = (desiredSet - currentSet).toList()
|
||||||
|
val toDel = (currentSet - desiredSet).toList()
|
||||||
|
|
||||||
|
if (toAdd.isNotEmpty()) {
|
||||||
|
val addResp = apiService.addShippingService(ShippingServiceRequest(couriers = toAdd))
|
||||||
|
if (!addResp.isSuccessful) {
|
||||||
|
Log.e(TAG, "Add couriers failed: ${addResp.code()} ${addResp.errorBody()?.string()}")
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "Added couriers: $toAdd")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toDel.isNotEmpty()) {
|
||||||
|
val delResp = apiService.deleteShippingService(ShippingServiceRequest(couriers = toDel))
|
||||||
|
if (!delResp.isSuccessful) {
|
||||||
|
Log.e(TAG, "Delete couriers failed: ${delResp.code()} ${delResp.errorBody()?.string()}")
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "Deleted couriers: $toDel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Sync shipping exception", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the profile response (already successful here)
|
||||||
|
profileResp
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Error updating store approval flow", e)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.toPlain(): RequestBody =
|
||||||
|
this.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
|
|
||||||
|
private fun createQrisPartOrNull(file: File?): MultipartBody.Part? =
|
||||||
|
file?.let {
|
||||||
|
val mime = when (it.extension.lowercase()) {
|
||||||
|
"jpg", "jpeg" -> "image/jpeg"
|
||||||
|
"png" -> "image/png"
|
||||||
|
else -> "application/octet-stream"
|
||||||
|
}.toMediaTypeOrNull()
|
||||||
|
|
||||||
|
MultipartBody.Part.createFormData(
|
||||||
|
"qris",
|
||||||
|
it.name,
|
||||||
|
it.asRequestBody(mime)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private var TAG = "MyStoreRepository"
|
private var TAG = "MyStoreRepository"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -47,6 +47,7 @@ import okhttp3.RequestBody
|
|||||||
import okhttp3.RequestBody.Companion.toRequestBody
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
|
import com.alya.ecommerce_serang.data.api.dto.PaymentUpdate
|
||||||
|
|
||||||
class RegisterStoreActivity : AppCompatActivity() {
|
class RegisterStoreActivity : AppCompatActivity() {
|
||||||
|
|
||||||
@ -58,6 +59,21 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
private lateinit var subdistrictAdapter: SubdsitrictAdapter
|
private lateinit var subdistrictAdapter: SubdsitrictAdapter
|
||||||
private lateinit var bankAdapter: BankAdapter
|
private lateinit var bankAdapter: BankAdapter
|
||||||
|
|
||||||
|
// pending values (filled from myStoreProfile once)
|
||||||
|
private var wantedProvinceId: Int? = null
|
||||||
|
private var wantedCityId: String? = null
|
||||||
|
private var wantedSubdistrictId: String? = null
|
||||||
|
private var wantedBankName: String? = null
|
||||||
|
|
||||||
|
// one-shot guards so we don't re-apply repeatedly
|
||||||
|
private var provinceApplied = false
|
||||||
|
private var cityApplied = false
|
||||||
|
private var subdistrictApplied = false
|
||||||
|
private var bankApplied = false
|
||||||
|
|
||||||
|
// avoid clearing/overriding while restoring
|
||||||
|
private var isRestoringSelections = false
|
||||||
|
|
||||||
// Request codes for file picking
|
// Request codes for file picking
|
||||||
private val PICK_STORE_IMAGE_REQUEST = 1001
|
private val PICK_STORE_IMAGE_REQUEST = 1001
|
||||||
private val PICK_KTP_REQUEST = 1002
|
private val PICK_KTP_REQUEST = 1002
|
||||||
@ -123,6 +139,10 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
setupSpinners() // Location spinners
|
setupSpinners() // Location spinners
|
||||||
Log.d(TAG, "onCreate: Spinners setup completed")
|
Log.d(TAG, "onCreate: Spinners setup completed")
|
||||||
|
|
||||||
|
binding.checkboxApprove.setOnCheckedChangeListener { _, _ ->
|
||||||
|
validateRequiredFields()
|
||||||
|
}
|
||||||
|
|
||||||
// Setup observers
|
// Setup observers
|
||||||
setupStoreTypesObserver() // Store type observer
|
setupStoreTypesObserver() // Store type observer
|
||||||
setupObservers()
|
setupObservers()
|
||||||
@ -209,12 +229,27 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
// Prefill spinner for store types
|
// Prefill spinner for store types
|
||||||
preselectStoreType(store.storeTypeId)
|
preselectStoreType(store.storeTypeId)
|
||||||
|
|
||||||
// Prefill province, city, and subdistrict
|
// Cache what we want to select later (after data arrives)
|
||||||
preselectProvinceCitySubdistrict(
|
wantedProvinceId = store.provinceId
|
||||||
provinceId = store.provinceId,
|
wantedCityId = store.cityId
|
||||||
cityId = store.cityId,
|
wantedSubdistrictId = store.subdistrict
|
||||||
subdistrictId = store.subdistrict
|
wantedBankName = storeResponse.payment.firstOrNull()?.bankName
|
||||||
)
|
|
||||||
|
// Cache what we want to select later (after data arrives)
|
||||||
|
wantedProvinceId = store.provinceId
|
||||||
|
wantedCityId = store.cityId
|
||||||
|
wantedSubdistrictId = store.subdistrict
|
||||||
|
wantedBankName = storeResponse.payment.firstOrNull()?.bankName
|
||||||
|
|
||||||
|
// Mark restoring flow on
|
||||||
|
isRestoringSelections = true
|
||||||
|
|
||||||
|
// Try to apply immediately (if adapters already have data), otherwise
|
||||||
|
// observers below will apply when data is ready.
|
||||||
|
tryApplyProvince()
|
||||||
|
tryApplyCity()
|
||||||
|
tryApplySubdistrict()
|
||||||
|
tryApplyBank()
|
||||||
|
|
||||||
validateRequiredFields()
|
validateRequiredFields()
|
||||||
}
|
}
|
||||||
@ -229,64 +264,78 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
else Toast.makeText(this, "Harap lengkapi semua field yang wajib diisi", Toast.LENGTH_SHORT).show()
|
else Toast.makeText(this, "Harap lengkapi semua field yang wajib diisi", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
validateRequiredFields()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun preselectStoreType(storeTypeId: Int) {
|
private fun preselectStoreType(storeTypeId: Int) {
|
||||||
// The adapter is created in setupStoreTypeSpinner(...)
|
// The adapter is created in setupStoreTypeSpinner(...)
|
||||||
val adapter = binding.spinnerStoreType.adapter
|
val adapter = binding.spinnerStoreType.adapter ?: return
|
||||||
if (adapter != null) {
|
for (i in 0 until adapter.count) {
|
||||||
val count = adapter.count
|
val item = adapter.getItem(i) as? StoreTypesItem
|
||||||
for (i in 0 until count) {
|
if (item?.id == storeTypeId) {
|
||||||
val item = adapter.getItem(i) as? StoreTypesItem
|
binding.spinnerStoreType.setSelection(i, false)
|
||||||
if (item?.id == storeTypeId) {
|
viewModel.storeTypeId.value = storeTypeId
|
||||||
binding.spinnerStoreType.setSelection(i, false)
|
validateRequiredFields()
|
||||||
break
|
break
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun preselectProvinceCitySubdistrict(
|
private fun tryApplyProvince() {
|
||||||
provinceId: Int,
|
if (provinceApplied) return
|
||||||
cityId: String,
|
val target = wantedProvinceId ?: return
|
||||||
subdistrictId: String
|
val count = provinceAdapter.count
|
||||||
) {
|
for (i in 0 until count) {
|
||||||
// Province first (this will trigger cities fetch)
|
if (provinceAdapter.getProvinceId(i) == target) {
|
||||||
val provCount = provinceAdapter.count
|
|
||||||
for (i in 0 until provCount) {
|
|
||||||
if (provinceAdapter.getProvinceId(i) == provinceId) {
|
|
||||||
binding.spinnerProvince.setSelection(i, false)
|
binding.spinnerProvince.setSelection(i, false)
|
||||||
break
|
provinceApplied = true
|
||||||
|
maybeFinishRestoring()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// When cities arrive, select the city, then load subdistricts
|
private fun tryApplyCity() {
|
||||||
viewModel.citiesState.observe(this) { state ->
|
if (cityApplied) return
|
||||||
if (state is Result.Success) {
|
val target = wantedCityId ?: return
|
||||||
val cityCount = cityAdapter.count
|
val count = cityAdapter.count
|
||||||
for (i in 0 until cityCount) {
|
for (i in 0 until count) {
|
||||||
if (cityAdapter.getCityId(i) == cityId) {
|
if (cityAdapter.getCityId(i) == target) {
|
||||||
binding.spinnerCity.setSelection(i, false)
|
binding.spinnerCity.setSelection(i, false)
|
||||||
break
|
cityApplied = true
|
||||||
}
|
maybeFinishRestoring()
|
||||||
}
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// When subdistricts arrive, select the subdistrict
|
private fun tryApplySubdistrict() {
|
||||||
viewModel.subdistrictState.observe(this) { state ->
|
if (subdistrictApplied) return
|
||||||
if (state is Result.Success) {
|
val target = wantedSubdistrictId ?: return
|
||||||
val subCount = subdistrictAdapter.count
|
val count = subdistrictAdapter.count
|
||||||
for (i in 0 until subCount) {
|
for (i in 0 until count) {
|
||||||
if (subdistrictAdapter.getSubdistrictId(i) == subdistrictId) {
|
if (subdistrictAdapter.getSubdistrictId(i) == target) {
|
||||||
binding.spinnerSubdistrict.setSelection(i, false)
|
binding.spinnerSubdistrict.setSelection(i, false)
|
||||||
break
|
subdistrictApplied = true
|
||||||
}
|
maybeFinishRestoring()
|
||||||
}
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun tryApplyBank() {
|
||||||
|
if (bankApplied) return
|
||||||
|
val targetName = wantedBankName ?: return
|
||||||
|
val pos = bankAdapter.findPositionByName(targetName)
|
||||||
|
if (pos >= 0) {
|
||||||
|
binding.spinnerBankName.setSelection(pos, false)
|
||||||
|
viewModel.bankName.value = targetName
|
||||||
|
viewModel.selectedBankName = targetName
|
||||||
|
validateRequiredFields()
|
||||||
|
bankApplied = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun setupHeader() {
|
private fun setupHeader() {
|
||||||
binding.header.main.background = ContextCompat.getColor(this, R.color.blue_500).toDrawable()
|
binding.header.main.background = ContextCompat.getColor(this, R.color.blue_500).toDrawable()
|
||||||
binding.header.headerTitle.visibility = View.GONE
|
binding.header.headerTitle.visibility = View.GONE
|
||||||
@ -301,31 +350,42 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun validateRequiredFields() {
|
private fun validateRequiredFields() {
|
||||||
val isFormValid = !viewModel.storeName.value.isNullOrBlank() &&
|
val bankName = viewModel.bankName.value?.trim().orEmpty()
|
||||||
!viewModel.street.value.isNullOrBlank() &&
|
val bankSelected = bankName.isNotEmpty() && !bankName.equals("Pilih Bank", ignoreCase = true)
|
||||||
(viewModel.postalCode.value ?: 0) > 0 &&
|
|
||||||
!viewModel.subdistrict.value.isNullOrBlank() &&
|
|
||||||
!viewModel.bankName.value.isNullOrBlank() &&
|
|
||||||
(viewModel.bankNumber.value ?: 0) > 0 &&
|
|
||||||
(viewModel.provinceId.value ?: 0) > 0 &&
|
|
||||||
!viewModel.cityId.value.isNullOrBlank() &&
|
|
||||||
(viewModel.storeTypeId.value ?: 0) > 0 &&
|
|
||||||
viewModel.ktpUri != null &&
|
|
||||||
viewModel.nibUri != null &&
|
|
||||||
viewModel.npwpUri != null &&
|
|
||||||
viewModel.selectedCouriers.isNotEmpty() &&
|
|
||||||
!viewModel.accountName.value.isNullOrBlank()
|
|
||||||
|
|
||||||
binding.btnRegister.isEnabled = true
|
val provinceSelected = viewModel.provinceId.value != null
|
||||||
if (isFormValid) {
|
val citySelected = !viewModel.cityId.value.isNullOrBlank()
|
||||||
binding.btnRegister.setBackgroundResource(R.drawable.bg_button_active)
|
val subdistrictSelected = !viewModel.subdistrict.value.isNullOrBlank()
|
||||||
binding.btnRegister.setTextColor(ContextCompat.getColor(this, R.color.white))
|
|
||||||
binding.btnRegister.isEnabled = true
|
val currentStoreType = binding.spinnerStoreType.selectedItem as? StoreTypesItem
|
||||||
} else {
|
val storeTypeSelected = when {
|
||||||
binding.btnRegister.setBackgroundResource(R.drawable.bg_button_disabled)
|
currentStoreType != null -> currentStoreType.id != 0 &&
|
||||||
binding.btnRegister.setTextColor(ContextCompat.getColor(this, R.color.black_300))
|
!currentStoreType.name.equals("Pilih Jenis UMKM", true)
|
||||||
binding.btnRegister.isEnabled = false
|
else -> (viewModel.storeTypeId.value ?: -1) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val isFormValid =
|
||||||
|
!viewModel.storeName.value.isNullOrBlank() &&
|
||||||
|
!viewModel.street.value.isNullOrBlank() &&
|
||||||
|
(viewModel.postalCode.value ?: 0) > 0 &&
|
||||||
|
provinceSelected && citySelected && subdistrictSelected &&
|
||||||
|
storeTypeSelected &&
|
||||||
|
bankSelected &&
|
||||||
|
(viewModel.bankNumber.value ?: 0) > 0 &&
|
||||||
|
viewModel.ktpUri != null &&
|
||||||
|
viewModel.nibUri != null &&
|
||||||
|
viewModel.npwpUri != null &&
|
||||||
|
viewModel.selectedCouriers.isNotEmpty() &&
|
||||||
|
!viewModel.accountName.value.isNullOrBlank() &&
|
||||||
|
binding.checkboxApprove.isChecked
|
||||||
|
|
||||||
|
binding.btnRegister.isEnabled = isFormValid
|
||||||
|
binding.btnRegister.setBackgroundResource(
|
||||||
|
if (isFormValid) R.drawable.bg_button_active else R.drawable.bg_button_disabled
|
||||||
|
)
|
||||||
|
binding.btnRegister.setTextColor(
|
||||||
|
ContextCompat.getColor(this, if (isFormValid) R.color.white else R.color.black_300)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupObservers() {
|
private fun setupObservers() {
|
||||||
@ -340,12 +400,10 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
binding.spinnerProvince.isEnabled = false
|
binding.spinnerProvince.isEnabled = false
|
||||||
}
|
}
|
||||||
is Result.Success -> {
|
is Result.Success -> {
|
||||||
Log.d(TAG, "setupObservers: Provinces loaded successfully: ${state.data.size} provinces")
|
|
||||||
binding.provinceProgressBar.visibility = View.GONE
|
binding.provinceProgressBar.visibility = View.GONE
|
||||||
binding.spinnerProvince.isEnabled = true
|
binding.spinnerProvince.isEnabled = true
|
||||||
|
|
||||||
// Update adapter with data
|
|
||||||
provinceAdapter.updateData(state.data)
|
provinceAdapter.updateData(state.data)
|
||||||
|
tryApplyProvince()
|
||||||
}
|
}
|
||||||
is Result.Error -> {
|
is Result.Error -> {
|
||||||
Log.e(TAG, "setupObservers: Error loading provinces: ${state.exception.message}")
|
Log.e(TAG, "setupObservers: Error loading provinces: ${state.exception.message}")
|
||||||
@ -364,12 +422,10 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
binding.spinnerCity.isEnabled = false
|
binding.spinnerCity.isEnabled = false
|
||||||
}
|
}
|
||||||
is Result.Success -> {
|
is Result.Success -> {
|
||||||
Log.d(TAG, "setupObservers: Cities loaded successfully: ${state.data.size} cities")
|
|
||||||
binding.cityProgressBar.visibility = View.GONE
|
binding.cityProgressBar.visibility = View.GONE
|
||||||
binding.spinnerCity.isEnabled = true
|
binding.spinnerCity.isEnabled = true
|
||||||
|
|
||||||
// Update adapter with data
|
|
||||||
cityAdapter.updateData(state.data)
|
cityAdapter.updateData(state.data)
|
||||||
|
tryApplyCity()
|
||||||
}
|
}
|
||||||
is Result.Error -> {
|
is Result.Error -> {
|
||||||
Log.e(TAG, "setupObservers: Error loading cities: ${state.exception.message}")
|
Log.e(TAG, "setupObservers: Error loading cities: ${state.exception.message}")
|
||||||
@ -387,11 +443,20 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
binding.spinnerSubdistrict.isEnabled = false
|
binding.spinnerSubdistrict.isEnabled = false
|
||||||
}
|
}
|
||||||
is Result.Success -> {
|
is Result.Success -> {
|
||||||
Log.d(TAG, "setupobservers: Subdistrict loaded successfullti: ${state.data.size} subdistrict")
|
|
||||||
binding.subdistrictProgressBar.visibility = View.GONE
|
binding.subdistrictProgressBar.visibility = View.GONE
|
||||||
binding.spinnerSubdistrict.isEnabled = true
|
binding.spinnerSubdistrict.isEnabled = true
|
||||||
|
|
||||||
subdistrictAdapter.updateData(state.data)
|
subdistrictAdapter.updateData(state.data)
|
||||||
|
|
||||||
|
// If you’re not restoring a specific subdistrict, select the first real item
|
||||||
|
if (!isRestoringSelections && state.data.isNotEmpty()) {
|
||||||
|
binding.spinnerSubdistrict.setSelection(0, false)
|
||||||
|
val id0 = subdistrictAdapter.getSubdistrictId(0)
|
||||||
|
viewModel.subdistrict.value = id0 ?: ""
|
||||||
|
viewModel.selectedSubdistrict = id0
|
||||||
|
validateRequiredFields()
|
||||||
|
}
|
||||||
|
|
||||||
|
tryApplySubdistrict()
|
||||||
}
|
}
|
||||||
is Result.Error -> {
|
is Result.Error -> {
|
||||||
Log.e(TAG, "setupObservers: Error loading subdistrict: ${state.exception.message}")
|
Log.e(TAG, "setupObservers: Error loading subdistrict: ${state.exception.message}")
|
||||||
@ -466,6 +531,7 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
// Setup spinner with API data
|
// Setup spinner with API data
|
||||||
setupStoreTypeSpinner(displayList)
|
setupStoreTypeSpinner(displayList)
|
||||||
|
tryApplyBank()
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "setupStoreTypesObserver: Received empty store types list")
|
Log.w(TAG, "setupStoreTypesObserver: Received empty store types list")
|
||||||
}
|
}
|
||||||
@ -510,17 +576,10 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
// Set item selection listener
|
// Set item selection listener
|
||||||
binding.spinnerStoreType.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
binding.spinnerStoreType.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
override fun onItemSelected(parent: AdapterView<*>?, view: View?, pos: Int, id: Long) {
|
||||||
val selectedItem = adapter.getItem(position)
|
val item = (binding.spinnerStoreType.adapter.getItem(pos) as? StoreTypesItem)
|
||||||
Log.d(TAG, "Store type selected: position=$position, item=${selectedItem?.name}, id=${selectedItem?.id}")
|
if (item != null && item.id > 0) viewModel.storeTypeId.value = item.id
|
||||||
|
validateRequiredFields()
|
||||||
if (selectedItem != null && selectedItem.id > 0) {
|
|
||||||
// Store the actual ID from the API, not just position
|
|
||||||
viewModel.storeTypeId.value = selectedItem.id
|
|
||||||
Log.d(TAG, "Set storeTypeId to ${selectedItem.id}")
|
|
||||||
} else {
|
|
||||||
Log.d(TAG, "Default or null store type selected, not setting storeTypeId")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNothingSelected(parent: AdapterView<*>?) {
|
override fun onNothingSelected(parent: AdapterView<*>?) {
|
||||||
@ -539,22 +598,12 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
// Setup province spinner
|
// Setup province spinner
|
||||||
binding.spinnerProvince.adapter = provinceAdapter
|
binding.spinnerProvince.adapter = provinceAdapter
|
||||||
binding.spinnerProvince.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
binding.spinnerProvince.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
override fun onItemSelected(parent: AdapterView<*>?, view: View?, pos: Int, id: Long) {
|
||||||
Log.d(TAG, "Province selected at position: $position")
|
provinceAdapter.getProvinceId(pos)?.let {
|
||||||
val provinceId = provinceAdapter.getProvinceId(position)
|
viewModel.provinceId.value = it
|
||||||
if (provinceId != null) {
|
viewModel.getCities(it)
|
||||||
Log.d(TAG, "Setting province ID: $provinceId")
|
|
||||||
viewModel.provinceId.value = provinceId
|
|
||||||
Log.d(TAG, "Fetching cities for province ID: $provinceId")
|
|
||||||
viewModel.getCities(provinceId)
|
|
||||||
|
|
||||||
// Reset city selection when province changes
|
|
||||||
Log.d(TAG, "Clearing city adapter for new province selection")
|
|
||||||
cityAdapter.clear()
|
|
||||||
binding.spinnerCity.setSelection(0)
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, "Invalid province ID for position: $position")
|
|
||||||
}
|
}
|
||||||
|
validateRequiredFields()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNothingSelected(parent: AdapterView<*>?) {
|
override fun onNothingSelected(parent: AdapterView<*>?) {
|
||||||
@ -565,21 +614,13 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
// Setup city spinner
|
// Setup city spinner
|
||||||
binding.spinnerCity.adapter = cityAdapter
|
binding.spinnerCity.adapter = cityAdapter
|
||||||
binding.spinnerCity.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
binding.spinnerCity.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
override fun onItemSelected(p: AdapterView<*>?, v: View?, pos: Int, id: Long) {
|
||||||
Log.d(TAG, "City selected at position: $position")
|
cityAdapter.getCityId(pos)?.let {
|
||||||
val cityId = cityAdapter.getCityId(position)
|
viewModel.cityId.value = it
|
||||||
if (cityId != null) {
|
viewModel.getSubdistrict(it)
|
||||||
Log.d(TAG, "Setting city ID: $cityId")
|
viewModel.selectedCityId = it
|
||||||
viewModel.cityId.value = cityId
|
|
||||||
Log.d(TAG, "Fetching subdistrict for city ID: $cityId")
|
|
||||||
viewModel.getSubdistrict(cityId)
|
|
||||||
|
|
||||||
subdistrictAdapter.clear()
|
|
||||||
binding.spinnerSubdistrict.setSelection(0)
|
|
||||||
viewModel.selectedCityId = cityId
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, "Invalid city ID for position: $position")
|
|
||||||
}
|
}
|
||||||
|
validateRequiredFields()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNothingSelected(parent: AdapterView<*>?) {
|
override fun onNothingSelected(parent: AdapterView<*>?) {
|
||||||
@ -590,16 +631,11 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
//Setup Subdistrict spinner
|
//Setup Subdistrict spinner
|
||||||
binding.spinnerSubdistrict.adapter = subdistrictAdapter
|
binding.spinnerSubdistrict.adapter = subdistrictAdapter
|
||||||
binding.spinnerSubdistrict.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
binding.spinnerSubdistrict.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
override fun onItemSelected(p: AdapterView<*>?, v: View?, pos: Int, id: Long) {
|
||||||
Log.d(TAG, "Subdistrict selected at position: $position")
|
val selectedId = subdistrictAdapter.getSubdistrictId(pos)
|
||||||
val subdistrictId = subdistrictAdapter.getSubdistrictId(position)
|
viewModel.subdistrict.value = selectedId ?: "" // empty => not selected
|
||||||
if (subdistrictId != null) {
|
viewModel.selectedSubdistrict = selectedId
|
||||||
Log.d(TAG, "Setting subdistrict ID: $subdistrictId")
|
validateRequiredFields()
|
||||||
viewModel.subdistrict.value = subdistrictId
|
|
||||||
viewModel.selectedSubdistrict = subdistrictId
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, "Invalid subdistrict ID for position: $position")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNothingSelected(parent: AdapterView<*>?) {
|
override fun onNothingSelected(parent: AdapterView<*>?) {
|
||||||
@ -609,63 +645,31 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
binding.spinnerBankName.adapter = bankAdapter
|
binding.spinnerBankName.adapter = bankAdapter
|
||||||
binding.spinnerBankName.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
binding.spinnerBankName.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
override fun onItemSelected(
|
override fun onItemSelected(p: AdapterView<*>?, v: View?, pos: Int, id: Long) {
|
||||||
parent: AdapterView<*>?,
|
val bankName = bankAdapter.getBankName(pos)
|
||||||
view: View?,
|
viewModel.bankName.value = bankName
|
||||||
position: Int,
|
viewModel.selectedBankName = bankName
|
||||||
id: Long
|
validateRequiredFields()
|
||||||
) {
|
|
||||||
Log.d(TAG, "Bank selected at position: $position")
|
|
||||||
val bankName = bankAdapter.getBankName(position)
|
|
||||||
if (bankName != null) {
|
|
||||||
Log.d(TAG, "Setting bank name: $bankName")
|
|
||||||
viewModel.bankName.value = bankName
|
|
||||||
viewModel.selectedBankName = bankName
|
|
||||||
|
|
||||||
// Optional: Log the selected bank details
|
|
||||||
val selectedBank = bankAdapter.getBankItem(position)
|
|
||||||
selectedBank?.let {
|
|
||||||
Log.d(TAG, "Selected bank: ${it.bankName} (Code: ${it.bankCode})")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hide progress bar if it was showing
|
|
||||||
binding.bankNameProgressBar.visibility = View.GONE
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, "Invalid bank name for position: $position")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onNothingSelected(parent: AdapterView<*>?) {
|
|
||||||
Log.d(TAG, "No bank selected")
|
|
||||||
viewModel.selectedBankName = null
|
|
||||||
}
|
}
|
||||||
|
override fun onNothingSelected(parent: AdapterView<*>?) { /* no-op */ }
|
||||||
}
|
}
|
||||||
|
tryApplyBank()
|
||||||
|
|
||||||
// Add initial hints to the spinners
|
// // Add initial hints to the spinners
|
||||||
if (provinceAdapter.isEmpty) {
|
// if (provinceAdapter.isEmpty) provinceAdapter.add("Pilih Provinsi")
|
||||||
Log.d(TAG, "Adding default province hint")
|
// if (cityAdapter.isEmpty) cityAdapter.add("Pilih Kabupaten/Kota")
|
||||||
provinceAdapter.add("Pilih Provinsi")
|
// if (subdistrictAdapter.isEmpty) subdistrictAdapter.add("Pilih Kecamatan")
|
||||||
}
|
// if (bankAdapter.isEmpty) bankAdapter.add("Pilih Bank")
|
||||||
|
|
||||||
if (cityAdapter.isEmpty) {
|
|
||||||
Log.d(TAG, "Adding default city hint")
|
|
||||||
cityAdapter.add("Pilih Kabupaten/Kota")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (subdistrictAdapter.isEmpty) {
|
|
||||||
Log.d(TAG, "Adding default kecamatan hint")
|
|
||||||
subdistrictAdapter.add("Pilih Kecamatan")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bankAdapter.isEmpty) {
|
|
||||||
Log.d(TAG, "Adding default bank hint")
|
|
||||||
bankAdapter.add("Pilih Bank")
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.d(TAG, "setupSpinners: Province and city spinners setup completed")
|
Log.d(TAG, "setupSpinners: Province and city spinners setup completed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun maybeFinishRestoring() {
|
||||||
|
if (provinceApplied && cityApplied && subdistrictApplied) {
|
||||||
|
isRestoringSelections = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun setupDocumentUploads() {
|
private fun setupDocumentUploads() {
|
||||||
Log.d(TAG, "setupDocumentUploads: Setting up document upload buttons")
|
Log.d(TAG, "setupDocumentUploads: Setting up document upload buttons")
|
||||||
|
|
||||||
@ -805,6 +809,7 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
override fun afterTextChanged(s: Editable?) {
|
override fun afterTextChanged(s: Editable?) {
|
||||||
viewModel.storeDescription.value = s.toString()
|
viewModel.storeDescription.value = s.toString()
|
||||||
Log.d(TAG, "Store description updated: ${s.toString().take(20)}${if ((s?.length ?: 0) > 20) "..." else ""}")
|
Log.d(TAG, "Store description updated: ${s.toString().take(20)}${if ((s?.length ?: 0) > 20) "..." else ""}")
|
||||||
|
validateRequiredFields()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -822,14 +827,8 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
||||||
override fun afterTextChanged(s: Editable?) {
|
override fun afterTextChanged(s: Editable?) {
|
||||||
try {
|
viewModel.postalCode.value = s.toString().toIntOrNull() ?: 0
|
||||||
viewModel.postalCode.value = s.toString().toInt()
|
validateRequiredFields()
|
||||||
Log.d(TAG, "Postal code updated: ${s.toString()}")
|
|
||||||
} catch (e: NumberFormatException) {
|
|
||||||
// Handle invalid input
|
|
||||||
Log.e(TAG, "Invalid postal code input: ${s.toString()}, error: $e")
|
|
||||||
validateRequiredFields()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -839,6 +838,7 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
override fun afterTextChanged(s: Editable?) {
|
override fun afterTextChanged(s: Editable?) {
|
||||||
viewModel.addressDetail.value = s.toString()
|
viewModel.addressDetail.value = s.toString()
|
||||||
Log.d(TAG, "Address detail updated: ${s.toString()}")
|
Log.d(TAG, "Address detail updated: ${s.toString()}")
|
||||||
|
validateRequiredFields()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -993,30 +993,41 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun doUpdateStoreProfile() {
|
private fun doUpdateStoreProfile() {
|
||||||
val nameBody: RequestBody = (viewModel.storeName.value ?: "")
|
// --- Text parts ---
|
||||||
.toRequestBody("text/plain".toMediaTypeOrNull())
|
val nameBody: RequestBody = (viewModel.storeName.value ?: "").toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
val typeBody: RequestBody = ((viewModel.storeTypeId.value ?: 0).toString())
|
val typeBody: RequestBody = ((viewModel.storeTypeId.value ?: 0).toString()).toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
.toRequestBody("text/plain".toMediaTypeOrNull())
|
val descBody: RequestBody = (viewModel.storeDescription.value ?: "").toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
val descBody: RequestBody = (viewModel.storeDescription.value ?: "")
|
// NOTE: is_on_leave is NOT part of approval multipart; keep separate if needed
|
||||||
.toRequestBody("text/plain".toMediaTypeOrNull())
|
|
||||||
val onLeaveBody: RequestBody = "false"
|
|
||||||
.toRequestBody("text/plain".toMediaTypeOrNull())
|
|
||||||
|
|
||||||
// --- Build Multipart for store image (optional) ---
|
val latBody: RequestBody = (viewModel.latitude.value ?: "").toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
// Prefer compressing images to keep payload small; fall back to raw copy if needed.
|
val longBody: RequestBody = (viewModel.longitude.value ?: "").toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
|
val provBody: RequestBody = ((viewModel.provinceId.value ?: 0).toString()).toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
|
val cityBody: RequestBody = (viewModel.cityId.value ?: "").toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
|
val subdistrictBody: RequestBody = (viewModel.selectedSubdistrict ?: viewModel.subdistrict.value ?: "").toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
|
|
||||||
|
// If you don't have village picker yet, send empty string or reuse subdistrict
|
||||||
|
val villageBody: RequestBody = "".toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
|
|
||||||
|
val streetBody: RequestBody = (viewModel.street.value ?: "").toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
|
val postalBody: RequestBody = ((viewModel.postalCode.value ?: 0).toString()).toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
|
val detailBody: RequestBody = (viewModel.addressDetail.value ?: "").toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
|
|
||||||
|
// You can read user phone from current store profile when reapply
|
||||||
|
val currentPhone = myStoreViewModel.myStoreProfile.value?.store?.userPhone ?: ""
|
||||||
|
val userPhoneBody: RequestBody = currentPhone.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
|
|
||||||
|
// --- Multipart images/docs (safe compress/copy) ---
|
||||||
val storeImgPart: MultipartBody.Part? = viewModel.storeImageUri?.let { uri ->
|
val storeImgPart: MultipartBody.Part? = viewModel.storeImageUri?.let { uri ->
|
||||||
try {
|
try {
|
||||||
// (A) Optional safety check: only allow jpg/png/webp
|
|
||||||
val allowed = Regex("^(jpg|jpeg|png|webp)$", RegexOption.IGNORE_CASE)
|
val allowed = Regex("^(jpg|jpeg|png|webp)$", RegexOption.IGNORE_CASE)
|
||||||
if (!ImageUtils.isAllowedFileType(this, uri, allowed)) {
|
if (!ImageUtils.isAllowedFileType(this, uri, allowed)) {
|
||||||
Toast.makeText(this, "Format gambar tidak didukung", Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, "Format gambar tidak didukung", Toast.LENGTH_SHORT).show()
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
// (B) Compress for upload (ke cacheDir), then build multipart
|
|
||||||
val compressed: File = ImageUtils.compressImage(
|
val compressed: File = ImageUtils.compressImage(
|
||||||
context = this,
|
context = this,
|
||||||
uri = uri,
|
uri = uri,
|
||||||
filename = "storeimg", // prefix
|
filename = "storeimg",
|
||||||
maxWidth = 1024,
|
maxWidth = 1024,
|
||||||
maxHeight = 1024,
|
maxHeight = 1024,
|
||||||
quality = 80
|
quality = 80
|
||||||
@ -1024,18 +1035,76 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
FileUtils.createMultipartFromFile("storeimg", compressed)
|
FileUtils.createMultipartFromFile("storeimg", compressed)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
// If compression fails, try raw copy as fallback
|
|
||||||
val rawFile = FileUtils.createTempFileFromUri(this, uri)
|
val rawFile = FileUtils.createTempFileFromUri(this, uri)
|
||||||
rawFile?.let { FileUtils.createMultipartFromFile("storeimg", it) }
|
rawFile?.let { FileUtils.createMultipartFromFile("storeimg", it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
myStoreViewModel.updateStoreProfile(
|
val ktpPart: MultipartBody.Part? = viewModel.ktpUri?.let { uri ->
|
||||||
|
val file = FileUtils.createTempFileFromUri(this, uri)
|
||||||
|
file?.let { FileUtils.createMultipartFromFile("ktp", it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
val npwpPart: MultipartBody.Part? = viewModel.npwpUri?.let { uri ->
|
||||||
|
val file = FileUtils.createTempFileFromUri(this, uri)
|
||||||
|
file?.let { FileUtils.createMultipartFromFile("npwp", it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
val nibPart: MultipartBody.Part? = viewModel.nibUri?.let { uri ->
|
||||||
|
val file = FileUtils.createTempFileFromUri(this, uri)
|
||||||
|
file?.let { FileUtils.createMultipartFromFile("nib", it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Couriers desired (sync to exactly this set) ---
|
||||||
|
val desiredCouriers = viewModel.selectedCouriers.toList()
|
||||||
|
|
||||||
|
// --- (Optional) Payment upsert from UI fields ---
|
||||||
|
// If you want to send the bank from the form during re-apply:
|
||||||
|
val paymentsToUpsert = buildList {
|
||||||
|
val bankName = viewModel.bankName.value
|
||||||
|
val bankNum = viewModel.bankNumber.value?.toString()
|
||||||
|
val accName = viewModel.accountName.value
|
||||||
|
|
||||||
|
if (!bankName.isNullOrBlank() && !bankNum.isNullOrBlank() && !accName.isNullOrBlank()) {
|
||||||
|
// If you want to update the first existing payment instead of adding new:
|
||||||
|
val existingId = myStoreViewModel.payment.value?.firstOrNull()?.id
|
||||||
|
add(
|
||||||
|
PaymentUpdate(
|
||||||
|
id = existingId, // null => add; id!=null => update
|
||||||
|
bankName = bankName,
|
||||||
|
bankNum = bankNum,
|
||||||
|
accountName = accName,
|
||||||
|
qrisImage = null // attach File if you have new QRIS to upload
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Delete list (empty if none) ---
|
||||||
|
val paymentIdToDelete = emptyList<Int>()
|
||||||
|
|
||||||
|
// --- Fire the update ---
|
||||||
|
myStoreViewModel.updateStoreApproval(
|
||||||
storeName = nameBody,
|
storeName = nameBody,
|
||||||
storeType = typeBody,
|
|
||||||
description = descBody,
|
description = descBody,
|
||||||
isOnLeave = onLeaveBody,
|
storeType = typeBody,
|
||||||
storeImage = storeImgPart
|
latitude = latBody,
|
||||||
|
longitude = longBody,
|
||||||
|
storeProvince = provBody,
|
||||||
|
storeCity = cityBody,
|
||||||
|
storeSubdistrict = subdistrictBody,
|
||||||
|
storeVillage = villageBody,
|
||||||
|
storeStreet = streetBody,
|
||||||
|
storePostalCode = postalBody,
|
||||||
|
storeAddressDetail = detailBody,
|
||||||
|
userPhone = userPhoneBody,
|
||||||
|
paymentsToUpdate = paymentsToUpsert,
|
||||||
|
paymentIdToDelete = paymentIdToDelete,
|
||||||
|
storeCourier = desiredCouriers,
|
||||||
|
storeImage = storeImgPart,
|
||||||
|
ktpImage = ktpPart,
|
||||||
|
npwpDocument = npwpPart,
|
||||||
|
nibDocument = nibPart
|
||||||
)
|
)
|
||||||
|
|
||||||
myStoreViewModel.updateStoreProfileResult.observe(this) {
|
myStoreViewModel.updateStoreProfileResult.observe(this) {
|
||||||
@ -1049,6 +1118,7 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "RegisterStoreActivity"
|
private const val TAG = "RegisterStoreActivity"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,6 +49,9 @@ class DetailStoreProductActivity : AppCompatActivity() {
|
|||||||
private var productId: Int? = null
|
private var productId: Int? = null
|
||||||
private var hasImage: Boolean = false
|
private var hasImage: Boolean = false
|
||||||
|
|
||||||
|
private var isEditing = false
|
||||||
|
private var hasExistingImage = false
|
||||||
|
|
||||||
private val viewModel: ProductViewModel by viewModels {
|
private val viewModel: ProductViewModel by viewModels {
|
||||||
BaseViewModelFactory {
|
BaseViewModelFactory {
|
||||||
sessionManager = SessionManager(this)
|
sessionManager = SessionManager(this)
|
||||||
@ -98,7 +101,7 @@ class DetailStoreProductActivity : AppCompatActivity() {
|
|||||||
binding = ActivityDetailStoreProductBinding.inflate(layoutInflater)
|
binding = ActivityDetailStoreProductBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
|
||||||
val isEditing = intent.getBooleanExtra("is_editing", false)
|
isEditing = intent.getBooleanExtra("is_editing", false)
|
||||||
productId = intent.getIntExtra("product_id", -1)
|
productId = intent.getIntExtra("product_id", -1)
|
||||||
|
|
||||||
binding.headerStoreProduct.headerTitle.text = if (isEditing) "Ubah Produk" else "Tambah Produk"
|
binding.headerStoreProduct.headerTitle.text = if (isEditing) "Ubah Produk" else "Tambah Produk"
|
||||||
@ -179,6 +182,7 @@ class DetailStoreProductActivity : AppCompatActivity() {
|
|||||||
binding.btnRemoveFoto.setOnClickListener {
|
binding.btnRemoveFoto.setOnClickListener {
|
||||||
imageUri = null
|
imageUri = null
|
||||||
hasImage = false
|
hasImage = false
|
||||||
|
hasExistingImage = false
|
||||||
binding.switcherFotoProduk.showPrevious()
|
binding.switcherFotoProduk.showPrevious()
|
||||||
validateForm()
|
validateForm()
|
||||||
}
|
}
|
||||||
@ -236,7 +240,9 @@ class DetailStoreProductActivity : AppCompatActivity() {
|
|||||||
} else product.image
|
} else product.image
|
||||||
Glide.with(this).load(imageUrl).into(binding.ivPreviewFoto)
|
Glide.with(this).load(imageUrl).into(binding.ivPreviewFoto)
|
||||||
binding.switcherFotoProduk.showNext()
|
binding.switcherFotoProduk.showNext()
|
||||||
|
|
||||||
hasImage = true
|
hasImage = true
|
||||||
|
hasExistingImage = true
|
||||||
|
|
||||||
// SPPIRT
|
// SPPIRT
|
||||||
product.sppirt?.let {
|
product.sppirt?.let {
|
||||||
@ -331,10 +337,18 @@ class DetailStoreProductActivity : AppCompatActivity() {
|
|||||||
val duration = binding.edtDurasi.text.toString().trim()
|
val duration = binding.edtDurasi.text.toString().trim()
|
||||||
val wholesaleMinItem = binding.edtMinPesanGrosir.text.toString().trim()
|
val wholesaleMinItem = binding.edtMinPesanGrosir.text.toString().trim()
|
||||||
val wholesalePrice = binding.edtHargaGrosir.text.toString().trim()
|
val wholesalePrice = binding.edtHargaGrosir.text.toString().trim()
|
||||||
val category = binding.spinnerKategoriProduk.selectedItemPosition != -1
|
val categorySelected = binding.spinnerKategoriProduk.selectedItemPosition != -1
|
||||||
val isPreOrderChecked = binding.switchIsPreOrder.isChecked
|
val isPreOrderChecked = binding.switchIsPreOrder.isChecked
|
||||||
val isWholesaleChecked = binding.switchIsWholesale.isChecked
|
val isWholesaleChecked = binding.switchIsWholesale.isChecked
|
||||||
|
|
||||||
|
val hasRequiredImage = if (isEditing) {
|
||||||
|
// In edit mode: allow existing server image OR newly picked image
|
||||||
|
hasImage || hasExistingImage
|
||||||
|
} else {
|
||||||
|
// In create mode: must have a picked image
|
||||||
|
hasImage
|
||||||
|
}
|
||||||
|
|
||||||
val valid = name.isNotEmpty() &&
|
val valid = name.isNotEmpty() &&
|
||||||
description.isNotEmpty() &&
|
description.isNotEmpty() &&
|
||||||
price.isNotEmpty() &&
|
price.isNotEmpty() &&
|
||||||
@ -343,8 +357,8 @@ class DetailStoreProductActivity : AppCompatActivity() {
|
|||||||
weight.isNotEmpty() &&
|
weight.isNotEmpty() &&
|
||||||
(!isPreOrderChecked || duration.isNotEmpty()) &&
|
(!isPreOrderChecked || duration.isNotEmpty()) &&
|
||||||
(!isWholesaleChecked || (wholesaleMinItem.isNotEmpty() && wholesalePrice.isNotEmpty())) &&
|
(!isWholesaleChecked || (wholesaleMinItem.isNotEmpty() && wholesalePrice.isNotEmpty())) &&
|
||||||
category &&
|
categorySelected &&
|
||||||
hasImage
|
hasRequiredImage
|
||||||
|
|
||||||
binding.btnSaveProduct.isEnabled = valid
|
binding.btnSaveProduct.isEnabled = valid
|
||||||
binding.btnSaveProduct.setTextColor(
|
binding.btnSaveProduct.setTextColor(
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import androidx.lifecycle.MutableLiveData
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.map
|
import androidx.lifecycle.map
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.alya.ecommerce_serang.data.api.dto.PaymentUpdate
|
||||||
import com.alya.ecommerce_serang.data.api.dto.ProductsItem
|
import com.alya.ecommerce_serang.data.api.dto.ProductsItem
|
||||||
import com.alya.ecommerce_serang.data.api.dto.Store
|
import com.alya.ecommerce_serang.data.api.dto.Store
|
||||||
import com.alya.ecommerce_serang.data.api.response.auth.StoreTypesItem
|
import com.alya.ecommerce_serang.data.api.response.auth.StoreTypesItem
|
||||||
@ -19,6 +20,7 @@ import kotlinx.coroutines.launch
|
|||||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||||
import okhttp3.MultipartBody
|
import okhttp3.MultipartBody
|
||||||
import okhttp3.RequestBody
|
import okhttp3.RequestBody
|
||||||
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
import java.text.NumberFormat
|
import java.text.NumberFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
@ -199,6 +201,80 @@ class MyStoreViewModel(private val repository: MyStoreRepository): ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun String.toRequestBody(): RequestBody =
|
private fun String.toPlain(): RequestBody =
|
||||||
RequestBody.create("text/plain".toMediaTypeOrNull(), this)
|
this.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
|
|
||||||
|
fun updateStoreApproval(
|
||||||
|
storeName: RequestBody,
|
||||||
|
description: RequestBody,
|
||||||
|
storeType: RequestBody,
|
||||||
|
latitude: RequestBody,
|
||||||
|
longitude: RequestBody,
|
||||||
|
storeProvince: RequestBody,
|
||||||
|
storeCity: RequestBody,
|
||||||
|
storeSubdistrict: RequestBody,
|
||||||
|
storeVillage: RequestBody,
|
||||||
|
storeStreet: RequestBody,
|
||||||
|
storePostalCode: RequestBody,
|
||||||
|
storeAddressDetail: RequestBody,
|
||||||
|
userPhone: RequestBody,
|
||||||
|
paymentsToUpdate: List<PaymentUpdate> = emptyList(),
|
||||||
|
paymentIdToDelete: List<Int> = emptyList(),
|
||||||
|
storeCourier: List<String>? = null,
|
||||||
|
storeImage: MultipartBody.Part?,
|
||||||
|
ktpImage: MultipartBody.Part?,
|
||||||
|
npwpDocument: MultipartBody.Part?,
|
||||||
|
nibDocument: MultipartBody.Part?
|
||||||
|
) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val store = myStoreProfile.value
|
||||||
|
|
||||||
|
if (store == null) {
|
||||||
|
_errorMessage.postValue("Data toko tidak tersedia")
|
||||||
|
Log.e(TAG, "Store data is null")
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
val response = repository.updateStoreApproval(
|
||||||
|
storeName = storeName,
|
||||||
|
description = description,
|
||||||
|
storeType = storeType,
|
||||||
|
latitude = latitude,
|
||||||
|
longitude = longitude,
|
||||||
|
storeProvince = storeProvince,
|
||||||
|
storeCity = storeCity,
|
||||||
|
storeSubdistrict = storeSubdistrict,
|
||||||
|
storeVillage = storeVillage,
|
||||||
|
storeStreet = storeStreet,
|
||||||
|
storePostalCode = storePostalCode,
|
||||||
|
storeAddressDetail = storeAddressDetail,
|
||||||
|
userPhone = userPhone,
|
||||||
|
paymentsToUpdate = paymentsToUpdate,
|
||||||
|
paymentIdToDelete = paymentIdToDelete,
|
||||||
|
storeCourier = storeCourier,
|
||||||
|
storeImage = storeImage,
|
||||||
|
ktpImage = ktpImage,
|
||||||
|
npwpDocument = npwpDocument,
|
||||||
|
nibDocument = nibDocument
|
||||||
|
)
|
||||||
|
|
||||||
|
if (response != null) {
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
_updateStoreProfileResult.postValue(response.body())
|
||||||
|
Log.d(TAG, "Update successful: ${response.body()}")
|
||||||
|
} else {
|
||||||
|
_errorMessage.postValue("Gagal memperbarui profil")
|
||||||
|
Log.e(TAG, "Update failed: ${response.errorBody()?.string()}")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_errorMessage.postValue("Terjadi kesalahan jaringan atau server")
|
||||||
|
Log.e(TAG, "Repository returned null response")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
_errorMessage.postValue(e.message ?: "Unexpected error")
|
||||||
|
Log.e(TAG, "Exception updating store profile", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -371,6 +371,7 @@
|
|||||||
android:background="@drawable/bg_text_field"
|
android:background="@drawable/bg_text_field"
|
||||||
android:hint="Isi stok produk di sini"
|
android:hint="Isi stok produk di sini"
|
||||||
android:padding="8dp"
|
android:padding="8dp"
|
||||||
|
android:inputType="number"
|
||||||
style="@style/body_small" />
|
style="@style/body_small" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@ -413,6 +414,7 @@
|
|||||||
android:hint="Isi minimum pemesanan produk di sini"
|
android:hint="Isi minimum pemesanan produk di sini"
|
||||||
android:padding="8dp"
|
android:padding="8dp"
|
||||||
style="@style/body_small"
|
style="@style/body_small"
|
||||||
|
android:inputType="number"
|
||||||
android:layout_marginTop="10dp" />
|
android:layout_marginTop="10dp" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@ -711,6 +713,7 @@
|
|||||||
android:background="@drawable/bg_text_field"
|
android:background="@drawable/bg_text_field"
|
||||||
android:hint="Isi minimum produk untuk mendapatkan harga grosir di sini"
|
android:hint="Isi minimum produk untuk mendapatkan harga grosir di sini"
|
||||||
android:padding="8dp"
|
android:padding="8dp"
|
||||||
|
android:inputType="number"
|
||||||
style="@style/body_small" />
|
style="@style/body_small" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@ -543,49 +543,6 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- Bank -->
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_marginBottom="24dp"
|
|
||||||
android:visibility="gone">
|
|
||||||
|
|
||||||
<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="10. Bank"
|
|
||||||
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/et_bank_name"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="@drawable/bg_text_field"
|
|
||||||
android:hint="Isi nama bank untuk toko Anda di sini"
|
|
||||||
android:padding="8dp"
|
|
||||||
style="@style/body_small"
|
|
||||||
android:layout_marginTop="10dp"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<!-- Nama Bank-->
|
<!-- Nama Bank-->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -654,6 +611,48 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Nama Pemilik 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="12. Nama Pemilik 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/et_account_name"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/bg_text_field"
|
||||||
|
android:hint="Isi nama pemilik rekening"
|
||||||
|
android:padding="8dp"
|
||||||
|
style="@style/body_small"
|
||||||
|
android:layout_marginTop="10dp"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- Nomor Rekening -->
|
<!-- Nomor Rekening -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -697,48 +696,6 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- Nama Pemilik 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="12. Nama Pemilik 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/et_account_name"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="@drawable/bg_text_field"
|
|
||||||
android:hint="Isi nama pemilik rekening"
|
|
||||||
android:padding="8dp"
|
|
||||||
style="@style/body_small"
|
|
||||||
android:layout_marginTop="10dp"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<!-- Kurir -->
|
<!-- Kurir -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -915,73 +872,6 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- NIB -->
|
|
||||||
<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="16. Dokumen NIB"
|
|
||||||
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>
|
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:id="@+id/container_nib"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="100dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:background="@android:drawable/editbox_background">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/img_nib"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:scaleType="centerInside"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/layout_upload_nib"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:gravity="center"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:layout_width="32dp"
|
|
||||||
android:layout_height="32dp"
|
|
||||||
android:src="@android:drawable/ic_menu_upload" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="Unggah dokumen Anda di sini"
|
|
||||||
android:textColor="#777777" />
|
|
||||||
</LinearLayout>
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<!-- NPWP -->
|
<!-- NPWP -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -1049,6 +939,73 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- NIB -->
|
||||||
|
<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="16. Dokumen NIB"
|
||||||
|
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>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/container_nib"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="100dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:background="@android:drawable/editbox_background">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/img_nib"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/layout_upload_nib"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:src="@android:drawable/ic_menu_upload" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="Unggah dokumen Anda di sini"
|
||||||
|
android:textColor="#777777" />
|
||||||
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
Reference in New Issue
Block a user