mirror of
https://github.com/shaulascr/ecommerce_serang.git
synced 2025-08-10 09:22:21 +00:00
Merge branch 'screen-features'
This commit is contained in:
@ -29,6 +29,9 @@
|
||||
android:theme="@style/Theme.Ecommerce_serang"
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".ui.auth.RegisterStoreActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.profile.editprofile.EditProfileCustActivity"
|
||||
android:exported="false" />
|
||||
@ -64,7 +67,7 @@
|
||||
|
||||
<activity
|
||||
android:name=".ui.profile.mystore.profile.shipping_service.ShippingServiceActivity"
|
||||
android:exported="false"/>
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.notif.NotificationActivity"
|
||||
android:exported="false" />
|
||||
@ -157,6 +160,7 @@
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_icon"
|
||||
android:resource="@drawable/outline_notifications_24" />
|
||||
@ -168,6 +172,4 @@
|
||||
android:value="fcm_default_channel" />
|
||||
</application>
|
||||
|
||||
|
||||
|
||||
</manifest>
|
@ -1,50 +1,17 @@
|
||||
package com.alya.ecommerce_serang.app
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import com.google.firebase.FirebaseApp
|
||||
import com.google.firebase.messaging.FirebaseMessaging
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
|
||||
@HiltAndroidApp
|
||||
class App : Application(){
|
||||
private val TAG = "AppSerang"
|
||||
// private val TAG = "AppSerang"
|
||||
//
|
||||
//// var tokenTes: String? = null
|
||||
//
|
||||
// override fun onCreate() {
|
||||
//
|
||||
// }
|
||||
|
||||
// var tokenTes: String? = null
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
// Initialize Firebase
|
||||
FirebaseApp.initializeApp(this)
|
||||
|
||||
// Request FCM token at app startup
|
||||
retrieveFCMToken()
|
||||
}
|
||||
|
||||
private fun retrieveFCMToken() {
|
||||
FirebaseMessaging.getInstance().token
|
||||
.addOnCompleteListener { task ->
|
||||
if (!task.isSuccessful) {
|
||||
Log.e(TAG, "Failed to get FCM token", task.exception)
|
||||
return@addOnCompleteListener
|
||||
}
|
||||
|
||||
val token = task.result
|
||||
// tokenTes = token
|
||||
Log.d(TAG, "FCM token retrieved: $token")
|
||||
|
||||
// Save token locally
|
||||
val sharedPreferences = getSharedPreferences("FCM_PREFS", Context.MODE_PRIVATE)
|
||||
sharedPreferences.edit().putString("FCM_TOKEN", token).apply()
|
||||
|
||||
// Send to your server
|
||||
sendTokenToServer(token)
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendTokenToServer(token: String) {
|
||||
Log.d(TAG, "Would send token to server: $token")
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.alya.ecommerce_serang.data.api.dto
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class CancelOrderReq (
|
||||
@SerializedName("order_id")
|
||||
val orderId: Int,
|
||||
|
||||
@SerializedName("reason")
|
||||
val reason: String
|
||||
)
|
@ -0,0 +1,8 @@
|
||||
package com.alya.ecommerce_serang.data.api.dto
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class FcmReq (
|
||||
@SerializedName("fcm_req")
|
||||
val fcmToken: String?= null
|
||||
)
|
@ -0,0 +1,11 @@
|
||||
package com.alya.ecommerce_serang.data.api.dto
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class VerifRegisReq (
|
||||
@SerializedName("field")
|
||||
val fieldRegis: String,
|
||||
|
||||
@SerializedName("value")
|
||||
val valueRegis: String
|
||||
)
|
@ -0,0 +1,9 @@
|
||||
package com.alya.ecommerce_serang.data.api.response.auth
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class FcmTokenResponse(
|
||||
|
||||
@field:SerializedName("message")
|
||||
val message: String? = null
|
||||
)
|
@ -0,0 +1,9 @@
|
||||
package com.alya.ecommerce_serang.data.api.response.auth
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class HasStoreResponse(
|
||||
|
||||
@field:SerializedName("hasStore")
|
||||
val hasStore: Boolean
|
||||
)
|
@ -0,0 +1,21 @@
|
||||
package com.alya.ecommerce_serang.data.api.response.auth
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class ListStoreTypeResponse(
|
||||
|
||||
@field:SerializedName("storeTypes")
|
||||
val storeTypes: List<StoreTypesItem>,
|
||||
|
||||
@field:SerializedName("message")
|
||||
val message: String
|
||||
)
|
||||
|
||||
data class StoreTypesItem(
|
||||
|
||||
@field:SerializedName("name")
|
||||
val name: String,
|
||||
|
||||
@field:SerializedName("id")
|
||||
val id: Int
|
||||
)
|
@ -0,0 +1,57 @@
|
||||
package com.alya.ecommerce_serang.data.api.response.auth
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class RegisterStoreResponse(
|
||||
|
||||
@field:SerializedName("store")
|
||||
val store: Store,
|
||||
|
||||
@field:SerializedName("message")
|
||||
val message: String
|
||||
)
|
||||
|
||||
data class Store(
|
||||
|
||||
@field:SerializedName("image")
|
||||
val image: String,
|
||||
|
||||
@field:SerializedName("ktp")
|
||||
val ktp: String,
|
||||
|
||||
@field:SerializedName("nib")
|
||||
val nib: String,
|
||||
|
||||
@field:SerializedName("npwp")
|
||||
val npwp: String,
|
||||
|
||||
@field:SerializedName("address_id")
|
||||
val addressId: Int,
|
||||
|
||||
@field:SerializedName("description")
|
||||
val description: String,
|
||||
|
||||
@field:SerializedName("store_type_id")
|
||||
val storeTypeId: Int,
|
||||
|
||||
@field:SerializedName("is_on_leave")
|
||||
val isOnLeave: Boolean,
|
||||
|
||||
@field:SerializedName("balance")
|
||||
val balance: String,
|
||||
|
||||
@field:SerializedName("user_id")
|
||||
val userId: Int,
|
||||
|
||||
@field:SerializedName("name")
|
||||
val name: String,
|
||||
|
||||
@field:SerializedName("persetujuan")
|
||||
val persetujuan: String,
|
||||
|
||||
@field:SerializedName("id")
|
||||
val id: Int,
|
||||
|
||||
@field:SerializedName("status")
|
||||
val status: String
|
||||
)
|
@ -0,0 +1,14 @@
|
||||
package com.alya.ecommerce_serang.data.api.response.customer.order
|
||||
|
||||
data class CancelOrderResponse(
|
||||
val data: DataCancel,
|
||||
val message: String
|
||||
)
|
||||
|
||||
data class DataCancel(
|
||||
val reason: String,
|
||||
val createdAt: String,
|
||||
val id: Int,
|
||||
val orderId: Int
|
||||
)
|
||||
|
@ -22,6 +22,9 @@ class ApiConfig {
|
||||
val client = OkHttpClient.Builder()
|
||||
.addInterceptor(loggingInterceptor)
|
||||
.addInterceptor(authInterceptor)
|
||||
.connectTimeout(60, TimeUnit.SECONDS) // Increase to 60 seconds
|
||||
.readTimeout(60, TimeUnit.SECONDS) // Increase to 60 seconds
|
||||
.writeTimeout(60, TimeUnit.SECONDS)
|
||||
.build()
|
||||
|
||||
val retrofit = Retrofit.Builder()
|
||||
|
@ -3,11 +3,13 @@ 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.CancelOrderReq
|
||||
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
|
||||
import com.alya.ecommerce_serang.data.api.dto.FcmReq
|
||||
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
|
||||
@ -20,10 +22,16 @@ 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.dto.VerifRegisReq
|
||||
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.HasStoreResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.ListStoreTypeResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.LoginResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.OtpResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.RegisterResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.RegisterStoreResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.VerifRegisterResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.chat.ChatHistoryResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.chat.ChatListResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.chat.SendChatResponse
|
||||
@ -32,6 +40,7 @@ import com.alya.ecommerce_serang.data.api.response.customer.cart.AddCartResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.cart.DeleteCartResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.cart.ListCartResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.cart.UpdateCartResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.CancelOrderResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.CourierCostResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.CreateOrderResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.CreateReviewResponse
|
||||
@ -73,6 +82,7 @@ import retrofit2.http.Multipart
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.PUT
|
||||
import retrofit2.http.Part
|
||||
import retrofit2.http.PartMap
|
||||
import retrofit2.http.Path
|
||||
import retrofit2.http.Query
|
||||
|
||||
@ -82,20 +92,54 @@ interface ApiService {
|
||||
@Body registerRequest: RegisterRequest
|
||||
): Response<RegisterResponse>
|
||||
|
||||
@POST("verif")
|
||||
suspend fun verifValue (
|
||||
@Body verifRegisReq: VerifRegisReq
|
||||
):VerifRegisterResponse
|
||||
|
||||
@GET("checkstore")
|
||||
suspend fun checkStore (): Response<CheckStoreResponse>
|
||||
|
||||
// @Multipart
|
||||
// @POST("registerstore")
|
||||
// suspend fun registerStore(
|
||||
//
|
||||
// ): Response<>
|
||||
@Multipart
|
||||
@POST("registerstore")
|
||||
suspend fun registerStore(
|
||||
@Part("description") description: RequestBody,
|
||||
@Part("store_type_id") storeTypeId: RequestBody,
|
||||
@Part("latitude") latitude: RequestBody,
|
||||
@Part("longitude") longitude: RequestBody,
|
||||
@Part("street") street: RequestBody,
|
||||
@Part("subdistrict") subdistrict: RequestBody,
|
||||
@Part("city_id") cityId: RequestBody,
|
||||
@Part("province_id") provinceId: RequestBody,
|
||||
@Part("postal_code") postalCode: RequestBody,
|
||||
@Part("detail") detail: RequestBody,
|
||||
@Part("bank_name") bankName: RequestBody,
|
||||
@Part("bank_num") bankNum: RequestBody,
|
||||
@Part("store_name") storeName: RequestBody,
|
||||
@Part storeimg: MultipartBody.Part?,
|
||||
@Part ktp: MultipartBody.Part?,
|
||||
@Part npwp: MultipartBody.Part?,
|
||||
@Part nib: MultipartBody.Part?,
|
||||
@Part persetujuan: MultipartBody.Part?,
|
||||
@PartMap couriers: Map<String, @JvmSuppressWildcards RequestBody>,
|
||||
@Part qris: MultipartBody.Part?,
|
||||
@Part("account_name") accountName: RequestBody,
|
||||
): Response<RegisterStoreResponse>
|
||||
|
||||
@POST("otp")
|
||||
suspend fun getOTP(
|
||||
@Body otpRequest: OtpRequest
|
||||
):OtpResponse
|
||||
|
||||
@PUT("updatefcm")
|
||||
suspend fun updateFcm(
|
||||
@Body fcmReq: FcmReq
|
||||
): FcmTokenResponse
|
||||
|
||||
@GET("checkstore")
|
||||
suspend fun checkStoreUser(
|
||||
): HasStoreResponse
|
||||
|
||||
@POST("login")
|
||||
suspend fun login(
|
||||
@Body loginRequest: LoginRequest
|
||||
@ -105,6 +149,10 @@ interface ApiService {
|
||||
suspend fun allCategory(
|
||||
): Response<CategoryResponse>
|
||||
|
||||
@GET("storetype")
|
||||
suspend fun listTypeStore(
|
||||
): Response<ListStoreTypeResponse>
|
||||
|
||||
@GET("product")
|
||||
suspend fun getAllProduct(): Response<AllProductResponse>
|
||||
|
||||
@ -131,6 +179,11 @@ interface ApiService {
|
||||
@Body request: OrderRequest
|
||||
): Response<CreateOrderResponse>
|
||||
|
||||
@POST("order/cancel")
|
||||
suspend fun cancelOrder(
|
||||
@Body cancelReq: CancelOrderReq
|
||||
): Response<CancelOrderResponse>
|
||||
|
||||
@GET("order/detail/{id}")
|
||||
suspend fun getDetailOrder(
|
||||
@Path("id") orderId: Int
|
||||
|
@ -2,16 +2,17 @@ package com.alya.ecommerce_serang.data.repository
|
||||
|
||||
import android.util.Log
|
||||
import com.alya.ecommerce_serang.data.api.dto.AddEvidenceMultipartRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.CancelOrderReq
|
||||
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
|
||||
import com.alya.ecommerce_serang.data.api.dto.OrderRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.OrderRequestBuy
|
||||
import com.alya.ecommerce_serang.data.api.dto.OrdersItem
|
||||
import com.alya.ecommerce_serang.data.api.dto.ReviewProductItem
|
||||
import com.alya.ecommerce_serang.data.api.dto.UpdateCart
|
||||
import com.alya.ecommerce_serang.data.api.dto.UserProfile
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.cart.DataItemCart
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.CancelOrderResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.CourierCostResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.CreateOrderResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.CreateReviewResponse
|
||||
@ -491,4 +492,23 @@ class OrderRepository(private val apiService: ApiService) {
|
||||
|
||||
}
|
||||
|
||||
suspend fun cancelOrder(cancelReq: CancelOrderReq): Result<CancelOrderResponse>{
|
||||
return try{
|
||||
val response= apiService.cancelOrder(cancelReq)
|
||||
|
||||
if (response.isSuccessful){
|
||||
response.body()?.let { cancelOrderResponse ->
|
||||
Result.Success(cancelOrderResponse)
|
||||
} ?: run {
|
||||
Result.Error(Exception("Failed to cancel order"))
|
||||
}
|
||||
} else {
|
||||
val errorMsg = response.errorBody()?.string() ?: "Unknown Error"
|
||||
Result.Error(Exception(errorMsg))
|
||||
}
|
||||
}catch (e: Exception){
|
||||
Result.Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -3,17 +3,30 @@ package com.alya.ecommerce_serang.data.repository
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import com.alya.ecommerce_serang.data.api.dto.FcmReq
|
||||
import com.alya.ecommerce_serang.data.api.dto.LoginRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.OtpRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.RegisterRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.UserProfile
|
||||
import com.alya.ecommerce_serang.data.api.dto.VerifRegisReq
|
||||
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.ListStoreTypeResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.LoginResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.OtpResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.RegisterStoreResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.VerifRegisterResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.ListCityResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.ListProvinceResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.profile.EditProfileResponse
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiService
|
||||
import com.alya.ecommerce_serang.utils.FileUtils
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import java.io.File
|
||||
|
||||
class UserRepository(private val apiService: ApiService) {
|
||||
//post data without message/response
|
||||
@ -21,6 +34,31 @@ class UserRepository(private val apiService: ApiService) {
|
||||
return apiService.getOTP(OtpRequest(email))
|
||||
}
|
||||
|
||||
suspend fun listStoreType(): Result<ListStoreTypeResponse>{
|
||||
return try{
|
||||
val response = apiService.listTypeStore()
|
||||
if (response.isSuccessful) {
|
||||
response.body()?.let {
|
||||
Result.Success(it)
|
||||
} ?: Result.Error(Exception("No store type"))
|
||||
} else {
|
||||
throw Exception("No response ${response.errorBody()?.string()}")
|
||||
}
|
||||
} catch (e:Exception){
|
||||
Result.Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getListProvinces(): ListProvinceResponse? {
|
||||
val response = apiService.getListProv()
|
||||
return if (response.isSuccessful) response.body() else null
|
||||
}
|
||||
|
||||
suspend fun getListCities(provId : Int): ListCityResponse? {
|
||||
val response = apiService.getCityProvId(provId)
|
||||
return if (response.isSuccessful) response.body() else null
|
||||
}
|
||||
|
||||
suspend fun registerUser(request: RegisterRequest): String {
|
||||
val response = apiService.register(request) // API call
|
||||
|
||||
@ -32,6 +70,169 @@ class UserRepository(private val apiService: ApiService) {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun registerStoreUser(
|
||||
context: Context,
|
||||
description: String,
|
||||
storeTypeId: Int,
|
||||
latitude: String,
|
||||
longitude: String,
|
||||
street: String,
|
||||
subdistrict: String,
|
||||
cityId: Int,
|
||||
provinceId: Int,
|
||||
postalCode: Int,
|
||||
detail: String?,
|
||||
bankName: String,
|
||||
bankNum: Int,
|
||||
storeName: String,
|
||||
storeImg: Uri?,
|
||||
ktp: Uri?,
|
||||
npwp: Uri?,
|
||||
nib: Uri?,
|
||||
persetujuan: Uri?,
|
||||
couriers: List<String>,
|
||||
qris: Uri?,
|
||||
accountName: String
|
||||
): Result<RegisterStoreResponse> {
|
||||
return try {
|
||||
val descriptionPart = description.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||
val storeTypeIdPart = storeTypeId.toString().toRequestBody("text/plain".toMediaTypeOrNull())
|
||||
val latitudePart = latitude.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||
val longitudePart = longitude.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||
val streetPart = street.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||
val subdistrictPart = subdistrict.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||
val cityIdPart = cityId.toString().toRequestBody("text/plain".toMediaTypeOrNull())
|
||||
val provinceIdPart = provinceId.toString().toRequestBody("text/plain".toMediaTypeOrNull())
|
||||
val postalCodePart = postalCode.toString().toRequestBody("text/plain".toMediaTypeOrNull())
|
||||
val detailPart = detail?.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||
val bankNamePart = bankName.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||
val bankNumPart = bankNum.toString().toRequestBody("text/plain".toMediaTypeOrNull())
|
||||
val storeNamePart = storeName.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||
val accountNamePart = accountName.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||
|
||||
|
||||
// Create a Map for courier values
|
||||
val courierMap = HashMap<String, RequestBody>()
|
||||
couriers.forEach { courier ->
|
||||
courierMap["couriers[]"] = courier.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||
}
|
||||
|
||||
// Convert URIs to MultipartBody.Part
|
||||
val storeImgPart = storeImg?.let {
|
||||
val inputStream = context.contentResolver.openInputStream(it)
|
||||
val file = File(context.cacheDir, "store_img_${System.currentTimeMillis()}")
|
||||
inputStream?.use { input ->
|
||||
file.outputStream().use { output ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
val mimeType = context.contentResolver.getType(it) ?: "application/octet-stream"
|
||||
val requestFile = file.asRequestBody(mimeType.toMediaTypeOrNull())
|
||||
MultipartBody.Part.createFormData("storeimg", file.name, requestFile)
|
||||
}
|
||||
|
||||
val ktpPart = ktp?.let {
|
||||
val inputStream = context.contentResolver.openInputStream(it)
|
||||
val file = File(context.cacheDir, "ktp_${System.currentTimeMillis()}")
|
||||
inputStream?.use { input ->
|
||||
file.outputStream().use { output ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
val mimeType = context.contentResolver.getType(it) ?: "application/octet-stream"
|
||||
val requestFile = file.asRequestBody(mimeType.toMediaTypeOrNull())
|
||||
MultipartBody.Part.createFormData("ktp", file.name, requestFile)
|
||||
}
|
||||
|
||||
val npwpPart = npwp?.let {
|
||||
val inputStream = context.contentResolver.openInputStream(it)
|
||||
val file = File(context.cacheDir, "npwp_${System.currentTimeMillis()}")
|
||||
inputStream?.use { input ->
|
||||
file.outputStream().use { output ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
val mimeType = context.contentResolver.getType(it) ?: "application/octet-stream"
|
||||
val requestFile = file.asRequestBody(mimeType.toMediaTypeOrNull())
|
||||
MultipartBody.Part.createFormData("npwp", file.name, requestFile)
|
||||
}
|
||||
|
||||
val nibPart = nib?.let {
|
||||
val inputStream = context.contentResolver.openInputStream(it)
|
||||
val file = File(context.cacheDir, "nib_${System.currentTimeMillis()}")
|
||||
inputStream?.use { input ->
|
||||
file.outputStream().use { output ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
val mimeType = context.contentResolver.getType(it) ?: "application/octet-stream"
|
||||
val requestFile = file.asRequestBody(mimeType.toMediaTypeOrNull())
|
||||
MultipartBody.Part.createFormData("nib", file.name, requestFile)
|
||||
}
|
||||
|
||||
val persetujuanPart = persetujuan?.let {
|
||||
val inputStream = context.contentResolver.openInputStream(it)
|
||||
val file = File(context.cacheDir, "persetujuan_${System.currentTimeMillis()}")
|
||||
inputStream?.use { input ->
|
||||
file.outputStream().use { output ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
val mimeType = context.contentResolver.getType(it) ?: "application/octet-stream"
|
||||
val requestFile = file.asRequestBody(mimeType.toMediaTypeOrNull())
|
||||
MultipartBody.Part.createFormData("persetujuan", file.name, requestFile)
|
||||
}
|
||||
|
||||
val qrisPart = qris?.let {
|
||||
val inputStream = context.contentResolver.openInputStream(it)
|
||||
val file = File(context.cacheDir, "qris_${System.currentTimeMillis()}")
|
||||
inputStream?.use { input ->
|
||||
file.outputStream().use { output ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
val mimeType = context.contentResolver.getType(it) ?: "application/octet-stream"
|
||||
val requestFile = file.asRequestBody(mimeType.toMediaTypeOrNull())
|
||||
MultipartBody.Part.createFormData("qris", file.name, requestFile)
|
||||
}
|
||||
|
||||
// Make the API call
|
||||
val response = apiService.registerStore(
|
||||
descriptionPart,
|
||||
storeTypeIdPart,
|
||||
latitudePart,
|
||||
longitudePart,
|
||||
streetPart,
|
||||
subdistrictPart,
|
||||
cityIdPart,
|
||||
provinceIdPart,
|
||||
postalCodePart,
|
||||
detailPart ?: "".toRequestBody("text/plain".toMediaTypeOrNull()),
|
||||
bankNamePart,
|
||||
bankNumPart,
|
||||
storeNamePart,
|
||||
storeImgPart,
|
||||
ktpPart,
|
||||
npwpPart,
|
||||
nibPart,
|
||||
persetujuanPart,
|
||||
courierMap,
|
||||
qrisPart,
|
||||
accountNamePart
|
||||
)
|
||||
|
||||
// Check if response is successful
|
||||
if (response.isSuccessful) {
|
||||
Result.Success(response.body() ?: throw Exception("Response body is null"))
|
||||
} else {
|
||||
Result.Error(Exception("Registration failed with code: ${response.code()}"))
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
Result.Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun login(email: String, password: String): Result<LoginResponse> {
|
||||
return try {
|
||||
val response = apiService.login(LoginRequest(email, password))
|
||||
@ -130,6 +331,18 @@ class UserRepository(private val apiService: ApiService) {
|
||||
Result.Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun checkStore(): HasStoreResponse{
|
||||
return apiService.checkStoreUser()
|
||||
}
|
||||
|
||||
suspend fun checkValue(request: VerifRegisReq): VerifRegisterResponse{
|
||||
return apiService.verifValue(request)
|
||||
}
|
||||
|
||||
suspend fun sendFcm(request: FcmReq): FcmTokenResponse{
|
||||
return apiService.updateFcm(request)
|
||||
}
|
||||
|
||||
companion object{
|
||||
private const val TAG = "UserRepository"
|
||||
|
@ -1,8 +1,10 @@
|
||||
package com.alya.ecommerce_serang.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
@ -20,11 +22,15 @@ import com.alya.ecommerce_serang.data.api.retrofit.ApiService
|
||||
import com.alya.ecommerce_serang.databinding.ActivityMainBinding
|
||||
import com.alya.ecommerce_serang.ui.notif.WebSocketManager
|
||||
import com.alya.ecommerce_serang.utils.SessionManager
|
||||
import com.google.firebase.FirebaseApp
|
||||
import com.google.firebase.messaging.FirebaseMessaging
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : AppCompatActivity() {
|
||||
private val TAG = "MainActivity"
|
||||
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
private lateinit var apiService: ApiService
|
||||
private lateinit var sessionManager: SessionManager
|
||||
@ -65,6 +71,11 @@ class MainActivity : AppCompatActivity() {
|
||||
)
|
||||
windowInsets
|
||||
}
|
||||
// Initialize Firebase
|
||||
FirebaseApp.initializeApp(this)
|
||||
|
||||
// Request FCM token at app startup
|
||||
retrieveFCMToken()
|
||||
|
||||
requestNotificationPermissionIfNeeded()
|
||||
|
||||
@ -151,4 +162,31 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun retrieveFCMToken() {
|
||||
FirebaseMessaging.getInstance().token
|
||||
.addOnCompleteListener { task ->
|
||||
if (!task.isSuccessful) {
|
||||
Log.e(TAG, "Failed to get FCM token", task.exception)
|
||||
return@addOnCompleteListener
|
||||
}
|
||||
|
||||
val token = task.result
|
||||
// tokenTes = token
|
||||
Log.d(TAG, "FCM token retrieved: $token")
|
||||
|
||||
// Save token locally
|
||||
val sharedPreferences = getSharedPreferences("FCM_PREFS", Context.MODE_PRIVATE)
|
||||
sharedPreferences.edit().putString("FCM_TOKEN", token).apply()
|
||||
|
||||
// Send to your server
|
||||
sendTokenToServer(token)
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendTokenToServer(token: String) {
|
||||
Log.d(TAG, "Would send token to server: $token")
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,11 +1,14 @@
|
||||
package com.alya.ecommerce_serang.ui.auth
|
||||
|
||||
import android.content.Context
|
||||
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.data.api.dto.FcmReq
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||
import com.alya.ecommerce_serang.data.repository.Result
|
||||
import com.alya.ecommerce_serang.data.repository.UserRepository
|
||||
@ -14,9 +17,13 @@ import com.alya.ecommerce_serang.ui.MainActivity
|
||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||
import com.alya.ecommerce_serang.utils.SessionManager
|
||||
import com.alya.ecommerce_serang.utils.viewmodel.LoginViewModel
|
||||
import com.google.firebase.FirebaseApp
|
||||
import com.google.firebase.messaging.FirebaseMessaging
|
||||
|
||||
class LoginActivity : AppCompatActivity() {
|
||||
|
||||
private val TAG = "LoginActivity"
|
||||
|
||||
private lateinit var binding: ActivityLoginBinding
|
||||
private val loginViewModel: LoginViewModel by viewModels{
|
||||
BaseViewModelFactory {
|
||||
@ -35,6 +42,11 @@ class LoginActivity : AppCompatActivity() {
|
||||
|
||||
setupListeners()
|
||||
observeLoginState()
|
||||
|
||||
FirebaseApp.initializeApp(this)
|
||||
|
||||
// Request FCM token at app startup
|
||||
retrieveFCMToken()
|
||||
}
|
||||
|
||||
private fun setupListeners() {
|
||||
@ -74,4 +86,35 @@ class LoginActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun retrieveFCMToken() {
|
||||
FirebaseMessaging.getInstance().token
|
||||
.addOnCompleteListener { task ->
|
||||
if (!task.isSuccessful) {
|
||||
Log.e(TAG, "Failed to get FCM token", task.exception)
|
||||
return@addOnCompleteListener
|
||||
}
|
||||
|
||||
val token = task.result
|
||||
// tokenTes = token
|
||||
Log.d(TAG, "FCM token retrieved: $token")
|
||||
|
||||
// Save token locally
|
||||
val sharedPreferences = getSharedPreferences("FCM_PREFS", Context.MODE_PRIVATE)
|
||||
sharedPreferences.edit().putString("FCM_TOKEN", token).apply()
|
||||
|
||||
// Send to your server
|
||||
sendTokenToServer(token)
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendTokenToServer(token: String) {
|
||||
Log.d(TAG, "Would send token to server: $token")
|
||||
val tokenFcm=FcmReq(
|
||||
fcmToken = token
|
||||
)
|
||||
loginViewModel.sendFcm(tokenFcm)
|
||||
Log.d(TAG, "Sent token fcm: $token")
|
||||
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.alya.ecommerce_serang.data.api.dto.RegisterRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.VerifRegisReq
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||
import com.alya.ecommerce_serang.data.repository.Result
|
||||
import com.alya.ecommerce_serang.data.repository.UserRepository
|
||||
@ -27,6 +28,17 @@ import java.util.Locale
|
||||
class RegisterActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityRegisterBinding
|
||||
private lateinit var sessionManager: SessionManager
|
||||
|
||||
private var isEmailValid = false
|
||||
private var isPhoneValid = false
|
||||
|
||||
// Track which validation was last performed
|
||||
private var lastCheckField = ""
|
||||
|
||||
// Counter for signup validation
|
||||
private var signupValidationsComplete = 0
|
||||
private var signupInProgress = false
|
||||
|
||||
private val registerViewModel: RegisterViewModel by viewModels{
|
||||
BaseViewModelFactory {
|
||||
val apiService = ApiConfig.getUnauthenticatedApiService()
|
||||
@ -76,107 +88,248 @@ class RegisterActivity : AppCompatActivity() {
|
||||
windowInsets
|
||||
}
|
||||
|
||||
setupObservers()
|
||||
|
||||
// Observe OTP state
|
||||
observeOtpState()
|
||||
// Set up field validations
|
||||
setupFieldValidations()
|
||||
|
||||
binding.btnSignup.setOnClickListener {
|
||||
// Retrieve values inside the click listener (so we get latest input)
|
||||
val birthDate = binding.etBirthDate.text.toString()
|
||||
val email = binding.etEmail.text.toString()
|
||||
val password = binding.etPassword.text.toString()
|
||||
val phone = binding.etNumberPhone.text.toString()
|
||||
val username = binding.etUsername.text.toString()
|
||||
val name = binding.etFullname.text.toString()
|
||||
val image = null
|
||||
|
||||
val userData = RegisterRequest(name, email, password, username, phone, birthDate, image)
|
||||
|
||||
Log.d("RegisterActivity", "Requesting OTP for email: $email")
|
||||
|
||||
// Request OTP and wait for success before showing dialog
|
||||
registerViewModel.requestOtp(userData.email.toString())
|
||||
|
||||
// Observe OTP state and show OTP dialog only when successful
|
||||
registerViewModel.otpState.observe(this) { result ->
|
||||
when (result) {
|
||||
is Result.Success -> {
|
||||
Log.d("RegisterActivity", "OTP sent successfully. Showing OTP dialog.")
|
||||
// Show OTP dialog after OTP is successfully sent
|
||||
val otpBottomSheet = OtpBottomSheetDialog(userData) { fullUserData ->
|
||||
Log.d("RegisterActivity", "OTP entered successfully. Proceeding with registration.")
|
||||
registerViewModel.registerUser(fullUserData) // Send complete data
|
||||
}
|
||||
otpBottomSheet.show(supportFragmentManager, "OtpBottomSheet")
|
||||
}
|
||||
is Result.Error -> {
|
||||
// Show error message if OTP request fails
|
||||
Log.e("RegisterActivity", "Failed to request OTP: ${result.exception.message}")
|
||||
Toast.makeText(this, "Failed to request OTP: ${result.exception.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
is Result.Loading -> {
|
||||
// Optional: Show loading indicator
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Observe Register state
|
||||
observeRegisterState()
|
||||
handleSignUp()
|
||||
}
|
||||
|
||||
binding.tvLoginAlt.setOnClickListener{
|
||||
binding.tvLoginAlt.setOnClickListener {
|
||||
val intent = Intent(this, LoginActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
binding.etBirthDate.setOnClickListener{
|
||||
binding.etBirthDate.setOnClickListener {
|
||||
showDatePicker()
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeOtpState() {
|
||||
registerViewModel.otpState.observe(this) { result ->
|
||||
when (result) {
|
||||
is Result.Loading -> {
|
||||
// Show loading indicator
|
||||
binding.progressBarOtp.visibility = android.view.View.VISIBLE
|
||||
}
|
||||
is Result.Success -> {
|
||||
// Hide loading indicator and show success message
|
||||
binding.progressBarOtp.visibility = android.view.View.GONE
|
||||
// Toast.makeText(this@RegisterActivity, result.data, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
is Result.Error -> {
|
||||
// Hide loading indicator and show error message
|
||||
binding.progressBarOtp.visibility = android.view.View.GONE
|
||||
Toast.makeText(this, "OTP Request Failed: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
private fun setupFieldValidations() {
|
||||
// Validate email when focus changes
|
||||
binding.etEmail.setOnFocusChangeListener { _, hasFocus ->
|
||||
if (!hasFocus) {
|
||||
val email = binding.etEmail.text.toString()
|
||||
if (email.isNotEmpty()) {
|
||||
validateEmail(email, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate phone when focus changes
|
||||
binding.etNumberPhone.setOnFocusChangeListener { _, hasFocus ->
|
||||
if (!hasFocus) {
|
||||
val phone = binding.etNumberPhone.text.toString()
|
||||
if (phone.isNotEmpty()) {
|
||||
validatePhone(phone, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeRegisterState() {
|
||||
registerViewModel.registerState.observe(this) { result ->
|
||||
when (result) {
|
||||
is Result.Loading -> {
|
||||
// Show loading indicator for registration
|
||||
binding.progressBarRegister.visibility = android.view.View.VISIBLE
|
||||
private fun validateEmail(email: String, isSignup: Boolean) {
|
||||
lastCheckField = "email"
|
||||
Log.d("RegisterActivity", "Validating email: $email (signup: $isSignup)")
|
||||
|
||||
val checkValueEmail = VerifRegisReq(
|
||||
fieldRegis = "email",
|
||||
valueRegis = email
|
||||
)
|
||||
registerViewModel.checkValueReg(checkValueEmail)
|
||||
}
|
||||
|
||||
private fun validatePhone(phone: String, isSignup: Boolean) {
|
||||
lastCheckField = "phone"
|
||||
Log.d("RegisterActivity", "Validating phone: $phone (signup: $isSignup)")
|
||||
|
||||
val checkValuePhone = VerifRegisReq(
|
||||
fieldRegis = "phone",
|
||||
valueRegis = phone
|
||||
)
|
||||
registerViewModel.checkValueReg(checkValuePhone)
|
||||
}
|
||||
|
||||
private fun setupObservers() {
|
||||
|
||||
registerViewModel.checkValue.observe(this) { result ->
|
||||
when (result) {
|
||||
is Result.Loading -> {
|
||||
// Show loading if needed
|
||||
}
|
||||
is Result.Success -> {
|
||||
val isValid = (result.data as? Boolean) ?: false
|
||||
|
||||
when (lastCheckField) {
|
||||
"email" -> {
|
||||
isEmailValid = isValid
|
||||
if (!isValid) {
|
||||
Toast.makeText(this, "Email is already registered", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Log.d("RegisterActivity", "Email is valid")
|
||||
}
|
||||
}
|
||||
"phone" -> {
|
||||
isPhoneValid = isValid
|
||||
if (!isValid) {
|
||||
Toast.makeText(this, "Phone number is already registered", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Log.d("RegisterActivity", "Phone is valid")
|
||||
}
|
||||
}
|
||||
}
|
||||
is Result.Success -> {
|
||||
// Hide loading indicator and show success message
|
||||
binding.progressBarRegister.visibility = android.view.View.GONE
|
||||
Toast.makeText(this, result.data, Toast.LENGTH_SHORT).show()
|
||||
val intent = Intent(this, LoginActivity::class.java)
|
||||
startActivity(intent)
|
||||
// Navigate to another screen if needed
|
||||
}
|
||||
is com.alya.ecommerce_serang.data.repository.Result.Error -> {
|
||||
// Hide loading indicator and show error message
|
||||
binding.progressBarRegister.visibility = android.view.View.GONE
|
||||
Toast.makeText(this, "Registration Failed: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
||||
|
||||
// Check if we're in signup process
|
||||
if (signupInProgress) {
|
||||
signupValidationsComplete++
|
||||
|
||||
// Check if both validations completed
|
||||
if (signupValidationsComplete >= 2) {
|
||||
signupInProgress = false
|
||||
signupValidationsComplete = 0
|
||||
|
||||
// If both validations passed, request OTP
|
||||
if (isEmailValid && isPhoneValid) {
|
||||
requestOtp()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is Result.Error -> {
|
||||
val fieldType = if (lastCheckField == "email") "Email" else "Phone"
|
||||
Toast.makeText(this, "$fieldType validation failed: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
||||
|
||||
// Mark validation as invalid
|
||||
if (lastCheckField == "email") {
|
||||
isEmailValid = false
|
||||
} else if (lastCheckField == "phone") {
|
||||
isPhoneValid = false
|
||||
}
|
||||
|
||||
// Update signup validation counter if in signup process
|
||||
if (signupInProgress) {
|
||||
signupValidationsComplete++
|
||||
|
||||
// Check if both validations completed
|
||||
if (signupValidationsComplete >= 2) {
|
||||
signupInProgress = false
|
||||
signupValidationsComplete = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
Log.e("RegisterActivity", "Unexpected result type: $result")
|
||||
}
|
||||
}
|
||||
}
|
||||
registerViewModel.otpState.observe(this) { result ->
|
||||
when (result) {
|
||||
is Result.Loading -> {
|
||||
binding.progressBarOtp.visibility = android.view.View.VISIBLE
|
||||
}
|
||||
is Result.Success -> {
|
||||
binding.progressBarOtp.visibility = android.view.View.GONE
|
||||
Log.d("RegisterActivity", "OTP sent successfully. Showing OTP dialog.")
|
||||
|
||||
// Create user data before showing OTP dialog
|
||||
val userData = createUserData()
|
||||
|
||||
// Show OTP dialog
|
||||
val otpBottomSheet = OtpBottomSheetDialog(userData) { fullUserData ->
|
||||
Log.d("RegisterActivity", "OTP entered successfully. Proceeding with registration.")
|
||||
registerViewModel.registerUser(fullUserData)
|
||||
}
|
||||
otpBottomSheet.show(supportFragmentManager, "OtpBottomSheet")
|
||||
}
|
||||
is Result.Error -> {
|
||||
binding.progressBarOtp.visibility = android.view.View.GONE
|
||||
Toast.makeText(this, "OTP Request Failed: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
else -> {
|
||||
Log.e("RegisterActivity", "Unexpected result type: $result")
|
||||
}
|
||||
}
|
||||
}
|
||||
registerViewModel.registerState.observe(this) { result ->
|
||||
when (result) {
|
||||
is Result.Loading -> {
|
||||
// Show loading indicator for registration
|
||||
binding.progressBarRegister.visibility = android.view.View.VISIBLE
|
||||
}
|
||||
is Result.Success -> {
|
||||
// Hide loading indicator and show success message
|
||||
binding.progressBarRegister.visibility = android.view.View.GONE
|
||||
Toast.makeText(this, result.data, Toast.LENGTH_SHORT).show()
|
||||
val intent = Intent(this, LoginActivity::class.java)
|
||||
startActivity(intent)
|
||||
// Navigate to another screen if needed
|
||||
}
|
||||
is com.alya.ecommerce_serang.data.repository.Result.Error -> {
|
||||
// Hide loading indicator and show error message
|
||||
binding.progressBarRegister.visibility = android.view.View.GONE
|
||||
Toast.makeText(this, "Registration Failed: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleSignUp() {
|
||||
// Basic validation first
|
||||
val email = binding.etEmail.text.toString()
|
||||
val password = binding.etPassword.text.toString()
|
||||
val confirmPassword = binding.etConfirmPassword.text.toString()
|
||||
val phone = binding.etNumberPhone.text.toString()
|
||||
val username = binding.etUsername.text.toString()
|
||||
val name = binding.etFullname.text.toString()
|
||||
val birthDate = binding.etBirthDate.text.toString()
|
||||
|
||||
// Check if fields are filled
|
||||
if (email.isEmpty() || password.isEmpty() || confirmPassword.isEmpty() ||
|
||||
phone.isEmpty() || username.isEmpty() || name.isEmpty() || birthDate.isEmpty()) {
|
||||
Toast.makeText(this, "Please fill all required fields", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
// Check if passwords match
|
||||
if (password != confirmPassword) {
|
||||
Toast.makeText(this, "Passwords do not match", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
// If both validations are already done and successful, just request OTP
|
||||
if (isEmailValid && isPhoneValid) {
|
||||
requestOtp()
|
||||
return
|
||||
}
|
||||
|
||||
// Reset validation counters
|
||||
signupInProgress = true
|
||||
signupValidationsComplete = 0
|
||||
|
||||
// Start validations in parallel
|
||||
validateEmail(email, true)
|
||||
validatePhone(phone, true)
|
||||
}
|
||||
|
||||
private fun requestOtp() {
|
||||
val email = binding.etEmail.text.toString()
|
||||
Log.d("RegisterActivity", "Requesting OTP for email: $email")
|
||||
registerViewModel.requestOtp(email)
|
||||
}
|
||||
|
||||
private fun createUserData(): RegisterRequest {
|
||||
// Get all form values
|
||||
val birthDate = binding.etBirthDate.text.toString()
|
||||
val email = binding.etEmail.text.toString()
|
||||
val password = binding.etPassword.text.toString()
|
||||
val phone = binding.etNumberPhone.text.toString()
|
||||
val username = binding.etUsername.text.toString()
|
||||
val name = binding.etFullname.text.toString()
|
||||
val image = null
|
||||
|
||||
// Create and return user data object
|
||||
return RegisterRequest(name, email, password, username, phone, birthDate, image)
|
||||
}
|
||||
|
||||
private fun showDatePicker() {
|
||||
@ -189,7 +342,7 @@ class RegisterActivity : AppCompatActivity() {
|
||||
this,
|
||||
{ _, selectedYear, selectedMonth, selectedDay ->
|
||||
calendar.set(selectedYear, selectedMonth, selectedDay)
|
||||
val sdf = SimpleDateFormat("dd-MM-yyyy", Locale.getDefault())
|
||||
val sdf = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
|
||||
binding.etBirthDate.setText(sdf.format(calendar.time))
|
||||
},
|
||||
year, month, day
|
||||
|
@ -0,0 +1,609 @@
|
||||
package com.alya.ecommerce_serang.ui.auth
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AdapterView
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.StoreTypesItem
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||
import com.alya.ecommerce_serang.data.repository.Result
|
||||
import com.alya.ecommerce_serang.data.repository.UserRepository
|
||||
import com.alya.ecommerce_serang.databinding.ActivityRegisterStoreBinding
|
||||
import com.alya.ecommerce_serang.ui.order.address.CityAdapter
|
||||
import com.alya.ecommerce_serang.ui.order.address.ProvinceAdapter
|
||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||
import com.alya.ecommerce_serang.utils.SessionManager
|
||||
|
||||
class RegisterStoreActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var binding: ActivityRegisterStoreBinding
|
||||
private lateinit var sessionManager: SessionManager
|
||||
|
||||
private lateinit var provinceAdapter: ProvinceAdapter
|
||||
private lateinit var cityAdapter: CityAdapter
|
||||
// Request codes for file picking
|
||||
private val PICK_STORE_IMAGE_REQUEST = 1001
|
||||
private val PICK_KTP_REQUEST = 1002
|
||||
private val PICK_NPWP_REQUEST = 1003
|
||||
private val PICK_NIB_REQUEST = 1004
|
||||
private val PICK_PERSETUJUAN_REQUEST = 1005
|
||||
private val PICK_QRIS_REQUEST = 1006
|
||||
|
||||
// Location request code
|
||||
private val LOCATION_PERMISSION_REQUEST = 2001
|
||||
|
||||
private val viewModel: RegisterStoreViewModel by viewModels {
|
||||
BaseViewModelFactory {
|
||||
val apiService = ApiConfig.getApiService(sessionManager)
|
||||
val orderRepository = UserRepository(apiService)
|
||||
RegisterStoreViewModel(orderRepository)
|
||||
}
|
||||
}
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityRegisterStoreBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
sessionManager = SessionManager(this)
|
||||
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
enableEdgeToEdge()
|
||||
|
||||
// Apply insets to your root layout
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view, windowInsets ->
|
||||
val systemBars = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
view.setPadding(
|
||||
systemBars.left,
|
||||
systemBars.top,
|
||||
systemBars.right,
|
||||
systemBars.bottom
|
||||
)
|
||||
windowInsets
|
||||
}
|
||||
|
||||
provinceAdapter = ProvinceAdapter(this)
|
||||
cityAdapter = CityAdapter(this)
|
||||
|
||||
setupDataBinding()
|
||||
setupSpinners() // Location spinners
|
||||
|
||||
// Setup observers
|
||||
setupStoreTypesObserver() // Store type observer
|
||||
setupObservers()
|
||||
|
||||
setupMap()
|
||||
setupDocumentUploads()
|
||||
setupCourierSelection()
|
||||
|
||||
viewModel.fetchStoreTypes()
|
||||
viewModel.getProvinces()
|
||||
|
||||
|
||||
// Setup register button
|
||||
binding.btnRegister.setOnClickListener {
|
||||
if (viewModel.validateForm()) {
|
||||
viewModel.registerStore(this)
|
||||
} else {
|
||||
Toast.makeText(this, "Harap lengkapi semua field yang wajib diisi", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupObservers() {
|
||||
// Observe province state
|
||||
viewModel.provincesState.observe(this) { state ->
|
||||
when (state) {
|
||||
is Result.Loading -> {
|
||||
Log.d(TAG, "Loading provinces...")
|
||||
binding.provinceProgressBar?.visibility = View.VISIBLE
|
||||
binding.spinnerProvince.isEnabled = false
|
||||
}
|
||||
is Result.Success -> {
|
||||
Log.d(TAG, "Provinces loaded: ${state.data.size}")
|
||||
binding.provinceProgressBar?.visibility = View.GONE
|
||||
binding.spinnerProvince.isEnabled = true
|
||||
|
||||
// Update adapter with data
|
||||
provinceAdapter.updateData(state.data)
|
||||
}
|
||||
is Result.Error -> {
|
||||
// Log.e(TAG, "Error loading provinces: ${state.}")
|
||||
binding.provinceProgressBar?.visibility = View.GONE
|
||||
binding.spinnerProvince.isEnabled = true
|
||||
|
||||
// Toast.makeText(this, "Gagal memuat provinsi: ${state.message}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Observe city state
|
||||
viewModel.citiesState.observe(this) { state ->
|
||||
when (state) {
|
||||
is Result.Loading -> {
|
||||
Log.d(TAG, "Loading cities...")
|
||||
binding.cityProgressBar?.visibility = View.VISIBLE
|
||||
binding.spinnerCity.isEnabled = false
|
||||
}
|
||||
is Result.Success -> {
|
||||
Log.d(TAG, "Cities loaded: ${state.data.size}")
|
||||
binding.cityProgressBar?.visibility = View.GONE
|
||||
binding.spinnerCity.isEnabled = true
|
||||
|
||||
// Update adapter with data
|
||||
cityAdapter.updateData(state.data)
|
||||
}
|
||||
is Result.Error -> {
|
||||
// Log.e(TAG, "Error loading cities: ${state.message}")
|
||||
binding.cityProgressBar?.visibility = View.GONE
|
||||
binding.spinnerCity.isEnabled = true
|
||||
|
||||
// Toast.makeText(this, "Gagal memuat kota: ${state.message}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Observe registration state
|
||||
viewModel.registerState.observe(this) { result ->
|
||||
when (result) {
|
||||
is Result.Loading -> {
|
||||
showLoading(true)
|
||||
}
|
||||
is Result.Success -> {
|
||||
showLoading(false)
|
||||
Toast.makeText(this, "Toko berhasil didaftarkan", Toast.LENGTH_SHORT).show()
|
||||
finish() // Return to previous screen
|
||||
}
|
||||
is Result.Error -> {
|
||||
showLoading(false)
|
||||
Toast.makeText(this, "Gagal mendaftarkan toko: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupStoreTypesObserver() {
|
||||
// Observe loading state
|
||||
viewModel.isLoadingType.observe(this) { isLoading ->
|
||||
if (isLoading) {
|
||||
// Show loading indicator for store types spinner
|
||||
binding.spinnerStoreType.isEnabled = false
|
||||
binding.storeTypeProgressBar?.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.spinnerStoreType.isEnabled = true
|
||||
binding.storeTypeProgressBar?.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
// Observe error messages
|
||||
viewModel.errorMessage.observe(this) { errorMsg ->
|
||||
if (errorMsg.isNotEmpty()) {
|
||||
Toast.makeText(this, "Error loading store types: $errorMsg", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
// Observe store types data
|
||||
viewModel.storeTypes.observe(this) { storeTypes ->
|
||||
Log.d(TAG, "Store types loaded: ${storeTypes.size}")
|
||||
if (storeTypes.isNotEmpty()) {
|
||||
// Add "Pilih Jenis UMKM" as the first item if it's not already there
|
||||
val displayList = if (storeTypes.any { it.name == "Pilih Jenis UMKM" || it.id == 0 }) {
|
||||
storeTypes
|
||||
} else {
|
||||
val defaultItem = StoreTypesItem(name = "Pilih Jenis UMKM", id = 0)
|
||||
listOf(defaultItem) + storeTypes
|
||||
}
|
||||
|
||||
// Setup spinner with API data
|
||||
setupStoreTypeSpinner(displayList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupStoreTypeSpinner(storeTypes: List<StoreTypesItem>) {
|
||||
Log.d(TAG, "Setting up store type spinner with ${storeTypes.size} items")
|
||||
|
||||
// Create a custom adapter to display just the name but hold the whole object
|
||||
val adapter = object : ArrayAdapter<StoreTypesItem>(
|
||||
this,
|
||||
android.R.layout.simple_spinner_item,
|
||||
storeTypes
|
||||
) {
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
val view = super.getView(position, convertView, parent)
|
||||
(view as TextView).text = getItem(position)?.name ?: ""
|
||||
return view
|
||||
}
|
||||
|
||||
override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
val view = super.getDropDownView(position, convertView, parent)
|
||||
(view as TextView).text = getItem(position)?.name ?: ""
|
||||
return view
|
||||
}
|
||||
|
||||
// Override toString to ensure proper display
|
||||
override fun getItem(position: Int): StoreTypesItem? {
|
||||
return super.getItem(position)
|
||||
}
|
||||
}
|
||||
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||
|
||||
// Set adapter to spinner
|
||||
binding.spinnerStoreType.adapter = adapter
|
||||
|
||||
// Set item selection listener
|
||||
binding.spinnerStoreType.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||
val selectedItem = adapter.getItem(position)
|
||||
Log.d(TAG, "Store type selected: position=$position, item=${selectedItem?.name}, id=${selectedItem?.id}")
|
||||
|
||||
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}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>?) {
|
||||
Log.d(TAG, "No store type selected")
|
||||
}
|
||||
}
|
||||
|
||||
// Hide progress bar after setup
|
||||
binding.storeTypeProgressBar?.visibility = View.GONE
|
||||
}
|
||||
|
||||
private fun setupSpinners() {
|
||||
// Setup province spinner
|
||||
binding.spinnerProvince.adapter = provinceAdapter
|
||||
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")
|
||||
val provinceId = provinceAdapter.getProvinceId(position)
|
||||
if (provinceId != null) {
|
||||
Log.d(TAG, "Setting province ID: $provinceId")
|
||||
viewModel.provinceId.value = provinceId
|
||||
viewModel.getCities(provinceId)
|
||||
|
||||
// Reset city selection when province changes
|
||||
cityAdapter.clear()
|
||||
binding.spinnerCity.setSelection(0)
|
||||
} else {
|
||||
Log.e(TAG, "Invalid province ID for position: $position")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>?) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
// Setup city spinner
|
||||
binding.spinnerCity.adapter = cityAdapter
|
||||
binding.spinnerCity.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||
Log.d(TAG, "City selected at position: $position")
|
||||
val cityId = cityAdapter.getCityId(position)
|
||||
if (cityId != null) {
|
||||
Log.d(TAG, "Setting city ID: $cityId")
|
||||
viewModel.cityId.value = cityId
|
||||
viewModel.selectedCityId = cityId
|
||||
} else {
|
||||
Log.e(TAG, "Invalid city ID for position: $position")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>?) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
// Add initial hints to the spinners
|
||||
if (provinceAdapter.isEmpty) {
|
||||
provinceAdapter.add("Pilih Provinsi")
|
||||
}
|
||||
|
||||
if (cityAdapter.isEmpty) {
|
||||
cityAdapter.add("Pilih Kabupaten/Kota")
|
||||
}
|
||||
}
|
||||
|
||||
// private fun setupSubdistrictSpinner(cityId: Int) {
|
||||
// // This would typically be populated from API based on cityId
|
||||
// val subdistricts = listOf("Pilih Kecamatan", "Kecamatan 1", "Kecamatan 2", "Kecamatan 3")
|
||||
// val subdistrictAdapter = ArrayAdapter(this, R.layout.simple_spinner_dropdown_item, subdistricts)
|
||||
// binding.spinnerSubdistrict.adapter = subdistrictAdapter
|
||||
// binding.spinnerSubdistrict.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||
// override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||
// if (position > 0) {
|
||||
// viewModel.subdistrict.value = subdistricts[position]
|
||||
// }
|
||||
// }
|
||||
// override fun onNothingSelected(parent: AdapterView<*>?) {}
|
||||
// }
|
||||
// }
|
||||
|
||||
private fun setupDocumentUploads() {
|
||||
// Store Image
|
||||
binding.containerStoreImg.setOnClickListener {
|
||||
pickImage(PICK_STORE_IMAGE_REQUEST)
|
||||
}
|
||||
|
||||
// KTP
|
||||
binding.containerKtp.setOnClickListener {
|
||||
pickImage(PICK_KTP_REQUEST)
|
||||
}
|
||||
|
||||
// NIB
|
||||
binding.containerNib.setOnClickListener {
|
||||
pickDocument(PICK_NIB_REQUEST)
|
||||
}
|
||||
|
||||
// NPWP
|
||||
binding.containerNpwp?.setOnClickListener {
|
||||
pickImage(PICK_NPWP_REQUEST)
|
||||
}
|
||||
|
||||
// SPPIRT
|
||||
binding.containerSppirt.setOnClickListener {
|
||||
pickDocument(PICK_PERSETUJUAN_REQUEST)
|
||||
}
|
||||
|
||||
// Halal
|
||||
binding.containerHalal.setOnClickListener {
|
||||
pickDocument(PICK_QRIS_REQUEST)
|
||||
}
|
||||
}
|
||||
|
||||
private fun pickImage(requestCode: Int) {
|
||||
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
|
||||
startActivityForResult(intent, requestCode)
|
||||
}
|
||||
|
||||
private fun pickDocument(requestCode: Int) {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
intent.type = "*/*"
|
||||
val mimeTypes = arrayOf("application/pdf", "image/jpeg", "image/png")
|
||||
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
|
||||
startActivityForResult(intent, requestCode)
|
||||
}
|
||||
|
||||
private fun setupCourierSelection() {
|
||||
binding.checkboxJne.setOnCheckedChangeListener { _, isChecked ->
|
||||
handleCourierSelection("jne", isChecked)
|
||||
}
|
||||
|
||||
binding.checkboxJnt.setOnCheckedChangeListener { _, isChecked ->
|
||||
handleCourierSelection("tiki", isChecked)
|
||||
}
|
||||
|
||||
binding.checkboxPos.setOnCheckedChangeListener { _, isChecked ->
|
||||
handleCourierSelection("pos", isChecked)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCourierSelection(courier: String, isSelected: Boolean) {
|
||||
if (isSelected) {
|
||||
if (!viewModel.selectedCouriers.contains(courier)) {
|
||||
viewModel.selectedCouriers.add(courier)
|
||||
}
|
||||
} else {
|
||||
viewModel.selectedCouriers.remove(courier)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupMap() {
|
||||
// This would typically integrate with Google Maps SDK
|
||||
// For simplicity, we're just using a placeholder
|
||||
binding.mapContainer.setOnClickListener {
|
||||
// Request location permission if not granted
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
this,
|
||||
Manifest.permission.ACCESS_FINE_LOCATION
|
||||
) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
ActivityCompat.requestPermissions(
|
||||
this,
|
||||
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
|
||||
LOCATION_PERMISSION_REQUEST
|
||||
|
||||
)
|
||||
viewModel.latitude.value = "-6.2088"
|
||||
viewModel.longitude.value = "106.8456"
|
||||
Toast.makeText(this, "Lokasi dipilih", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
// Show map selection UI
|
||||
// This would typically launch Maps UI for location selection
|
||||
// For now, we'll just set some dummy coordinates
|
||||
viewModel.latitude.value = "-6.2088"
|
||||
viewModel.longitude.value = "106.8456"
|
||||
Toast.makeText(this, "Lokasi dipilih", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupDataBinding() {
|
||||
// Two-way data binding for text fields
|
||||
binding.etStoreName.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
viewModel.storeName.value = s.toString()
|
||||
}
|
||||
})
|
||||
|
||||
binding.etStoreDescription.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
viewModel.storeDescription.value = s.toString()
|
||||
}
|
||||
})
|
||||
|
||||
binding.etStreet.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
viewModel.street.value = s.toString()
|
||||
}
|
||||
})
|
||||
|
||||
binding.etPostalCode.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
try {
|
||||
viewModel.postalCode.value = s.toString().toInt()
|
||||
} catch (e: NumberFormatException) {
|
||||
// Handle invalid input
|
||||
//show toast
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
binding.etAddressDetail.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
viewModel.addressDetail.value = s.toString()
|
||||
}
|
||||
})
|
||||
|
||||
binding.etBankNumber.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
viewModel.bankNumber.value = s.toString().toInt()
|
||||
}
|
||||
})
|
||||
|
||||
binding.etSubdistrict.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
viewModel.subdistrict.value = s.toString()
|
||||
}
|
||||
})
|
||||
|
||||
binding.etBankName.addTextChangedListener(object: TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
viewModel.subdistrict.value = s.toString()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
val uri = data.data
|
||||
when (requestCode) {
|
||||
PICK_STORE_IMAGE_REQUEST -> {
|
||||
viewModel.storeImageUri = uri
|
||||
updateImagePreview(uri, binding.imgStore, binding.layoutUploadStoreImg)
|
||||
}
|
||||
PICK_KTP_REQUEST -> {
|
||||
viewModel.ktpUri = uri
|
||||
updateImagePreview(uri, binding.imgKtp, binding.layoutUploadKtp)
|
||||
}
|
||||
PICK_NPWP_REQUEST -> {
|
||||
viewModel.npwpUri = uri
|
||||
updateDocumentPreview(binding.layoutUploadNpwp)
|
||||
}
|
||||
PICK_NIB_REQUEST -> {
|
||||
viewModel.nibUri = uri
|
||||
updateDocumentPreview(binding.layoutUploadNib)
|
||||
}
|
||||
PICK_PERSETUJUAN_REQUEST -> {
|
||||
viewModel.persetujuanUri = uri
|
||||
updateDocumentPreview(binding.layoutUploadSppirt)
|
||||
}
|
||||
PICK_QRIS_REQUEST -> {
|
||||
viewModel.qrisUri = uri
|
||||
updateDocumentPreview(binding.layoutUploadHalal)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateImagePreview(uri: Uri?, imageView: ImageView, uploadLayout: LinearLayout) {
|
||||
uri?.let {
|
||||
imageView.setImageURI(it)
|
||||
imageView.visibility = View.VISIBLE
|
||||
uploadLayout.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateDocumentPreview(uploadLayout: LinearLayout) {
|
||||
// For documents, we just show a success indicator
|
||||
val checkIcon = ImageView(this)
|
||||
checkIcon.setImageResource(android.R.drawable.ic_menu_gallery)
|
||||
val successText = TextView(this)
|
||||
successText.text = "Dokumen berhasil diunggah"
|
||||
|
||||
uploadLayout.removeAllViews()
|
||||
uploadLayout.addView(checkIcon)
|
||||
uploadLayout.addView(successText)
|
||||
}
|
||||
|
||||
//later implement get location form gps
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<out String>,
|
||||
grantResults: IntArray
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
if (requestCode == LOCATION_PERMISSION_REQUEST) {
|
||||
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
// Permission granted, proceed with location selection
|
||||
viewModel.latitude.value = "-6.2088"
|
||||
viewModel.longitude.value = "106.8456"
|
||||
Toast.makeText(this, "Lokasi dipilih", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
viewModel.latitude.value = "-6.2088"
|
||||
viewModel.longitude.value = "106.8456"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showLoading(isLoading: Boolean) {
|
||||
if (isLoading) {
|
||||
// Show loading indicator
|
||||
binding.btnRegister.isEnabled = false
|
||||
binding.btnRegister.text = "Mendaftar..."
|
||||
} else {
|
||||
// Hide loading indicator
|
||||
binding.btnRegister.isEnabled = true
|
||||
binding.btnRegister.text = "Daftar"
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "RegisterStoreActivity"
|
||||
}
|
||||
}
|
@ -0,0 +1,202 @@
|
||||
package com.alya.ecommerce_serang.ui.auth
|
||||
|
||||
import android.content.Context
|
||||
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.response.auth.RegisterStoreResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.StoreTypesItem
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.CitiesItem
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.ProvincesItem
|
||||
import com.alya.ecommerce_serang.data.repository.Result
|
||||
import com.alya.ecommerce_serang.data.repository.UserRepository
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class RegisterStoreViewModel(
|
||||
private val repository: UserRepository
|
||||
) : ViewModel() {
|
||||
|
||||
// LiveData for UI state
|
||||
private val _registerState = MutableLiveData<com.alya.ecommerce_serang.data.repository.Result<RegisterStoreResponse>>()
|
||||
val registerState: LiveData<com.alya.ecommerce_serang.data.repository.Result<RegisterStoreResponse>> = _registerState
|
||||
|
||||
private val _storeTypes = MutableLiveData<List<StoreTypesItem>>()
|
||||
val storeTypes: LiveData<List<StoreTypesItem>> = _storeTypes
|
||||
|
||||
// LiveData for error messages
|
||||
private val _errorMessage = MutableLiveData<String>()
|
||||
val errorMessage: LiveData<String> = _errorMessage
|
||||
|
||||
// LiveData for loading state
|
||||
private val _isLoadingType = MutableLiveData<Boolean>()
|
||||
val isLoadingType: LiveData<Boolean> = _isLoadingType
|
||||
|
||||
private val _provincesState = MutableLiveData<Result<List<ProvincesItem>>>()
|
||||
val provincesState: LiveData<Result<List<ProvincesItem>>> = _provincesState
|
||||
|
||||
private val _citiesState = MutableLiveData<Result<List<CitiesItem>>>()
|
||||
val citiesState: LiveData<Result<List<CitiesItem>>> = _citiesState
|
||||
|
||||
var selectedProvinceId: Int? = null
|
||||
var selectedCityId: Int? = null
|
||||
|
||||
// Form fields
|
||||
val storeName = MutableLiveData<String>()
|
||||
val storeDescription = MutableLiveData<String>()
|
||||
val storeTypeId = MutableLiveData<Int>()
|
||||
val latitude = MutableLiveData<String>()
|
||||
val longitude = MutableLiveData<String>()
|
||||
val street = MutableLiveData<String>()
|
||||
val subdistrict = MutableLiveData<String>()
|
||||
val cityId = MutableLiveData<Int>()
|
||||
val provinceId = MutableLiveData<Int>()
|
||||
val postalCode = MutableLiveData<Int>()
|
||||
val addressDetail = MutableLiveData<String>()
|
||||
val bankName = MutableLiveData<String>()
|
||||
val bankNumber = MutableLiveData<Int>()
|
||||
val accountName = MutableLiveData<String>()
|
||||
|
||||
// Files
|
||||
var storeImageUri: Uri? = null
|
||||
var ktpUri: Uri? = null
|
||||
var npwpUri: Uri? = null
|
||||
var nibUri: Uri? = null
|
||||
var persetujuanUri: Uri? = null
|
||||
var qrisUri: Uri? = null
|
||||
|
||||
// Selected couriers
|
||||
val selectedCouriers = mutableListOf<String>()
|
||||
|
||||
fun registerStore(context: Context) {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
_registerState.value = Result.Loading
|
||||
|
||||
val result = repository.registerStoreUser(
|
||||
context = context,
|
||||
description = storeDescription.value ?: "",
|
||||
storeTypeId = storeTypeId.value ?: 0,
|
||||
latitude = latitude.value ?: "",
|
||||
longitude = longitude.value ?: "",
|
||||
street = street.value ?: "",
|
||||
subdistrict = subdistrict.value ?: "",
|
||||
cityId = cityId.value ?: 0,
|
||||
provinceId = provinceId.value ?: 0,
|
||||
postalCode = postalCode.value ?: 0,
|
||||
detail = addressDetail.value,
|
||||
bankName = bankName.value ?: "",
|
||||
bankNum = bankNumber.value ?: 0,
|
||||
storeName = storeName.value ?: "",
|
||||
storeImg = storeImageUri,
|
||||
ktp = ktpUri,
|
||||
npwp = npwpUri,
|
||||
nib = nibUri,
|
||||
persetujuan = persetujuanUri,
|
||||
couriers = selectedCouriers,
|
||||
qris = qrisUri,
|
||||
accountName = accountName.value ?: ""
|
||||
)
|
||||
|
||||
_registerState.value = result
|
||||
} catch (e: Exception) {
|
||||
_registerState.value = com.alya.ecommerce_serang.data.repository.Result.Error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// // Helper function to convert Uri to File
|
||||
// private fun getFileFromUri(context: Context, uri: Uri): File {
|
||||
// val inputStream = context.contentResolver.openInputStream(uri)
|
||||
// val tempFile = File(context.cacheDir, "temp_file_${System.currentTimeMillis()}")
|
||||
// inputStream?.use { input ->
|
||||
// tempFile.outputStream().use { output ->
|
||||
// input.copyTo(output)
|
||||
// }
|
||||
// }
|
||||
// return tempFile
|
||||
// }
|
||||
|
||||
fun validateForm(): Boolean {
|
||||
// Implement form validation logic
|
||||
return !(storeName.value.isNullOrEmpty() ||
|
||||
storeTypeId.value == null ||
|
||||
street.value.isNullOrEmpty() ||
|
||||
subdistrict.value.isNullOrEmpty() ||
|
||||
cityId.value == null ||
|
||||
provinceId.value == null ||
|
||||
postalCode.value == null ||
|
||||
bankName.value.isNullOrEmpty() ||
|
||||
bankNumber.value == null ||
|
||||
selectedCouriers.isEmpty() ||
|
||||
ktpUri == null ||
|
||||
nibUri == null)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Function to fetch store types
|
||||
fun fetchStoreTypes() {
|
||||
_isLoadingType.value = true
|
||||
viewModelScope.launch {
|
||||
when (val result = repository.listStoreType()) {
|
||||
is Result.Success -> {
|
||||
_storeTypes.value = result.data.storeTypes
|
||||
_isLoadingType.value = false
|
||||
}
|
||||
is Result.Error -> {
|
||||
_errorMessage.value = result.exception.message ?: "Unknown error occurred"
|
||||
_isLoadingType.value = false
|
||||
}
|
||||
is Result.Loading -> {
|
||||
_isLoadingType.value = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getProvinces() {
|
||||
_provincesState.value = Result.Loading
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val result = repository.getListProvinces()
|
||||
if (result?.provinces != null) {
|
||||
_provincesState.postValue(Result.Success(result.provinces))
|
||||
Log.d(TAG, "Provinces loaded: ${result.provinces.size}")
|
||||
} else {
|
||||
_provincesState.postValue(Result.Error(Exception("Failed to load provinces")))
|
||||
Log.e(TAG, "Province result was null or empty")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
_provincesState.postValue(Result.Error(Exception(e.message ?: "Error loading provinces")))
|
||||
Log.e(TAG, "Error fetching provinces", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getCities(provinceId: Int){
|
||||
_citiesState.value = Result.Loading
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
selectedProvinceId = provinceId
|
||||
val result = repository.getListCities(provinceId)
|
||||
result?.let {
|
||||
_citiesState.postValue(Result.Success(it.cities))
|
||||
Log.d(TAG, "Cities loaded for province $provinceId: ${it.cities.size}")
|
||||
} ?: run {
|
||||
_citiesState.postValue(Result.Error(Exception("Failed to load cities")))
|
||||
Log.e(TAG, "City result was null for province $provinceId")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
_citiesState.postValue(Result.Error(Exception(e.message ?: "Error loading cities")))
|
||||
Log.e(TAG, "Error fetching cities for province $provinceId", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "RegisterStoreUserViewModel"
|
||||
}
|
||||
}
|
@ -125,6 +125,7 @@ class ChatActivity : AppCompatActivity() {
|
||||
val productRating = intent.getFloatExtra(Constants.EXTRA_PRODUCT_RATING, 0f)
|
||||
val storeName = intent.getStringExtra(Constants.EXTRA_STORE_NAME) ?: ""
|
||||
val chatRoomId = intent.getIntExtra(Constants.EXTRA_CHAT_ROOM_ID, 0)
|
||||
val storeImg = intent.getStringExtra(Constants.EXTRA_STORE_IMAGE) ?: ""
|
||||
|
||||
// Check if user is logged in
|
||||
val token = sessionManager.getToken()
|
||||
@ -137,7 +138,20 @@ class ChatActivity : AppCompatActivity() {
|
||||
return
|
||||
}
|
||||
|
||||
// Set chat parameters to ViewModel
|
||||
binding.tvStoreName.text = storeName
|
||||
val fullImageUrl = when (val img = storeImg) {
|
||||
is String -> {
|
||||
if (img.startsWith("/")) BASE_URL + img.substring(1) else img
|
||||
}
|
||||
else -> R.drawable.placeholder_image
|
||||
}
|
||||
|
||||
Glide.with(this)
|
||||
.load(fullImageUrl)
|
||||
.placeholder(R.drawable.placeholder_image)
|
||||
.into(binding.imgProfile)
|
||||
|
||||
// Set chat parameters to ViewModel
|
||||
viewModel.setChatParameters(
|
||||
storeId = storeId,
|
||||
productId = productId,
|
||||
@ -227,6 +241,7 @@ class ChatActivity : AppCompatActivity() {
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
// Observe state changes using LiveData
|
||||
viewModel.state.observe(this, Observer { state ->
|
||||
// Update messages
|
||||
@ -244,6 +259,7 @@ class ChatActivity : AppCompatActivity() {
|
||||
binding.ratingBar.rating = state.productRating
|
||||
binding.tvRating.text = state.productRating.toString()
|
||||
binding.tvSellerName.text = state.storeName
|
||||
binding.tvStoreName.text=state.storeName
|
||||
|
||||
// Load product image
|
||||
if (!state.productImageUrl.isNullOrEmpty()) {
|
||||
@ -270,6 +286,7 @@ class ChatActivity : AppCompatActivity() {
|
||||
binding.editTextMessage.hint = getString(R.string.write_message)
|
||||
}
|
||||
|
||||
|
||||
// Show typing indicator
|
||||
binding.tvTypingIndicator.visibility =
|
||||
if (state.isOtherUserTyping) View.VISIBLE else View.GONE
|
||||
@ -467,7 +484,8 @@ class ChatActivity : AppCompatActivity() {
|
||||
productImage: String? = null,
|
||||
productRating: String? = null,
|
||||
storeName: String? = null,
|
||||
chatRoomId: Int = 0
|
||||
chatRoomId: Int = 0,
|
||||
storeImage: String? = null
|
||||
) {
|
||||
val intent = Intent(context, ChatActivity::class.java).apply {
|
||||
putExtra(Constants.EXTRA_STORE_ID, storeId)
|
||||
@ -475,6 +493,7 @@ class ChatActivity : AppCompatActivity() {
|
||||
putExtra(Constants.EXTRA_PRODUCT_NAME, productName)
|
||||
putExtra(Constants.EXTRA_PRODUCT_PRICE, productPrice)
|
||||
putExtra(Constants.EXTRA_PRODUCT_IMAGE, productImage)
|
||||
putExtra(Constants.EXTRA_STORE_IMAGE, storeImage)
|
||||
|
||||
// Convert productRating string to float if provided
|
||||
if (productRating != null) {
|
||||
|
@ -1,337 +0,0 @@
|
||||
//package com.alya.ecommerce_serang.ui.chat
|
||||
//
|
||||
//import android.Manifest
|
||||
//import android.app.Activity
|
||||
//import android.content.Intent
|
||||
//import android.content.pm.PackageManager
|
||||
//import android.net.Uri
|
||||
//import android.os.Bundle
|
||||
//import android.provider.MediaStore
|
||||
//import android.text.Editable
|
||||
//import android.text.TextWatcher
|
||||
//import androidx.fragment.app.Fragment
|
||||
//import android.view.LayoutInflater
|
||||
//import android.view.View
|
||||
//import android.view.ViewGroup
|
||||
//import android.widget.Toast
|
||||
//import androidx.activity.result.contract.ActivityResultContracts
|
||||
//import androidx.core.app.ActivityCompat
|
||||
//import androidx.core.content.ContextCompat
|
||||
//import androidx.core.content.FileProvider
|
||||
//import androidx.fragment.app.viewModels
|
||||
//import androidx.lifecycle.lifecycleScope
|
||||
//import androidx.navigation.fragment.navArgs
|
||||
//import androidx.recyclerview.widget.LinearLayoutManager
|
||||
//import com.alya.ecommerce_serang.BuildConfig.BASE_URL
|
||||
//import com.alya.ecommerce_serang.R
|
||||
//import com.alya.ecommerce_serang.databinding.FragmentChatBinding
|
||||
//import com.alya.ecommerce_serang.utils.Constants
|
||||
//import com.bumptech.glide.Glide
|
||||
//import dagger.hilt.android.AndroidEntryPoint
|
||||
//import kotlinx.coroutines.launch
|
||||
//import java.io.File
|
||||
//import java.text.SimpleDateFormat
|
||||
//import java.util.Locale
|
||||
//
|
||||
//@AndroidEntryPoint
|
||||
//class ChatFragment : Fragment() {
|
||||
//
|
||||
// private var _binding: FragmentChatBinding? = null
|
||||
// private val binding get() = _binding!!
|
||||
//
|
||||
// private val viewModel: ChatViewModel by viewModels()
|
||||
//// private val args: ChatFragmentArgs by navArgs()
|
||||
//
|
||||
// private lateinit var chatAdapter: ChatAdapter
|
||||
//
|
||||
// // For image attachment
|
||||
// private var tempImageUri: Uri? = null
|
||||
//
|
||||
// // Typing indicator handler
|
||||
// private val typingHandler = android.os.Handler(android.os.Looper.getMainLooper())
|
||||
// private val stopTypingRunnable = Runnable {
|
||||
// viewModel.sendTypingStatus(false)
|
||||
// }
|
||||
//
|
||||
// // Activity Result Launchers
|
||||
// private val pickImageLauncher = registerForActivityResult(
|
||||
// ActivityResultContracts.StartActivityForResult()
|
||||
// ) { result ->
|
||||
// if (result.resultCode == Activity.RESULT_OK) {
|
||||
// result.data?.data?.let { uri ->
|
||||
// handleSelectedImage(uri)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private val takePictureLauncher = registerForActivityResult(
|
||||
// ActivityResultContracts.StartActivityForResult()
|
||||
// ) { result ->
|
||||
// if (result.resultCode == Activity.RESULT_OK) {
|
||||
// tempImageUri?.let { uri ->
|
||||
// handleSelectedImage(uri)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override fun onCreateView(
|
||||
// inflater: LayoutInflater,
|
||||
// container: ViewGroup?,
|
||||
// savedInstanceState: Bundle?
|
||||
// ): View {
|
||||
// _binding = FragmentChatBinding.inflate(inflater, container, false)
|
||||
// return binding.root
|
||||
// }
|
||||
//
|
||||
// override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
// super.onViewCreated(view, savedInstanceState)
|
||||
//
|
||||
// setupRecyclerView()
|
||||
// setupListeners()
|
||||
// setupTypingIndicator()
|
||||
// observeViewModel()
|
||||
// }
|
||||
//
|
||||
// private fun setupRecyclerView() {
|
||||
// chatAdapter = ChatAdapter()
|
||||
// binding.recyclerChat.apply {
|
||||
// adapter = chatAdapter
|
||||
// layoutManager = LinearLayoutManager(requireContext()).apply {
|
||||
// stackFromEnd = true
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private fun setupListeners() {
|
||||
// // Back button
|
||||
// binding.btnBack.setOnClickListener {
|
||||
// requireActivity().onBackPressed()
|
||||
// }
|
||||
//
|
||||
// // Options button
|
||||
// binding.btnOptions.setOnClickListener {
|
||||
// showOptionsMenu()
|
||||
// }
|
||||
//
|
||||
// // Send button
|
||||
// binding.btnSend.setOnClickListener {
|
||||
// val message = binding.editTextMessage.text.toString().trim()
|
||||
// if (message.isNotEmpty() || viewModel.state.value.hasAttachment) {
|
||||
// viewModel.sendMessage(message)
|
||||
// binding.editTextMessage.text.clear()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Attachment button
|
||||
// binding.btnAttachment.setOnClickListener {
|
||||
// checkPermissionsAndShowImagePicker()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private fun setupTypingIndicator() {
|
||||
// binding.editTextMessage.addTextChangedListener(object : TextWatcher {
|
||||
// override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
//
|
||||
// override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||
// viewModel.sendTypingStatus(true)
|
||||
//
|
||||
// // Reset the timer
|
||||
// typingHandler.removeCallbacks(stopTypingRunnable)
|
||||
// typingHandler.postDelayed(stopTypingRunnable, 1000)
|
||||
// }
|
||||
//
|
||||
// override fun afterTextChanged(s: Editable?) {}
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// private fun observeViewModel() {
|
||||
// viewLifecycleOwner.lifecycleScope.launch {
|
||||
// viewModel.state.collectLatest { state ->
|
||||
// // Update messages
|
||||
// chatAdapter.submitList(state.messages)
|
||||
//
|
||||
// // Scroll to bottom if new message
|
||||
// if (state.messages.isNotEmpty()) {
|
||||
// binding.recyclerChat.scrollToPosition(state.messages.size - 1)
|
||||
// }
|
||||
//
|
||||
// // Update product info
|
||||
// binding.tvProductName.text = state.productName
|
||||
// binding.tvProductPrice.text = state.productPrice
|
||||
// binding.ratingBar.rating = state.productRating
|
||||
// binding.tvRating.text = state.productRating.toString()
|
||||
// binding.tvSellerName.text = state.storeName
|
||||
//
|
||||
// // Load product image
|
||||
// if (state.productImageUrl.isNotEmpty()) {
|
||||
// Glide.with(requireContext())
|
||||
// .load(BASE_URL + state.productImageUrl)
|
||||
// .centerCrop()
|
||||
// .placeholder(R.drawable.placeholder_image)
|
||||
// .error(R.drawable.placeholder_image)
|
||||
// .into(binding.imgProduct)
|
||||
// }
|
||||
//
|
||||
// // Show/hide loading indicators
|
||||
// binding.progressBar.visibility = if (state.isLoading) View.VISIBLE else View.GONE
|
||||
// binding.btnSend.isEnabled = !state.isSending
|
||||
//
|
||||
// // Update attachment hint
|
||||
// if (state.hasAttachment) {
|
||||
// binding.editTextMessage.hint = getString(R.string.image_attached)
|
||||
// } else {
|
||||
// binding.editTextMessage.hint = getString(R.string.write_message)
|
||||
// }
|
||||
//
|
||||
// // Show typing indicator
|
||||
// binding.tvTypingIndicator.visibility =
|
||||
// if (state.isOtherUserTyping) View.VISIBLE else View.GONE
|
||||
//
|
||||
// // Handle connection state
|
||||
// handleConnectionState(state.connectionState)
|
||||
//
|
||||
// // Show error if any
|
||||
// state.error?.let { error ->
|
||||
// Toast.makeText(requireContext(), error, Toast.LENGTH_SHORT).show()
|
||||
// viewModel.clearError()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private fun handleConnectionState(state: ConnectionState) {
|
||||
// when (state) {
|
||||
// is ConnectionState.Connected -> {
|
||||
// binding.tvConnectionStatus.visibility = View.GONE
|
||||
// }
|
||||
// is ConnectionState.Connecting -> {
|
||||
// binding.tvConnectionStatus.visibility = View.VISIBLE
|
||||
// binding.tvConnectionStatus.text = getString(R.string.connecting)
|
||||
// }
|
||||
// is ConnectionState.Disconnected -> {
|
||||
// binding.tvConnectionStatus.visibility = View.VISIBLE
|
||||
// binding.tvConnectionStatus.text = getString(R.string.disconnected_reconnecting)
|
||||
// }
|
||||
// is ConnectionState.Error -> {
|
||||
// binding.tvConnectionStatus.visibility = View.VISIBLE
|
||||
// binding.tvConnectionStatus.text = getString(R.string.connection_error, state.message)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private fun showOptionsMenu() {
|
||||
// val options = arrayOf(
|
||||
// getString(R.string.block_user),
|
||||
// getString(R.string.report),
|
||||
// getString(R.string.clear_chat),
|
||||
// getString(R.string.cancel)
|
||||
// )
|
||||
//
|
||||
// androidx.appcompat.app.AlertDialog.Builder(requireContext())
|
||||
// .setTitle(getString(R.string.options))
|
||||
// .setItems(options) { dialog, which ->
|
||||
// when (which) {
|
||||
// 0 -> Toast.makeText(requireContext(), R.string.block_user_selected, Toast.LENGTH_SHORT).show()
|
||||
// 1 -> Toast.makeText(requireContext(), R.string.report_selected, Toast.LENGTH_SHORT).show()
|
||||
// 2 -> Toast.makeText(requireContext(), R.string.clear_chat_selected, Toast.LENGTH_SHORT).show()
|
||||
// }
|
||||
// dialog.dismiss()
|
||||
// }
|
||||
// .show()
|
||||
// }
|
||||
//
|
||||
// private fun checkPermissionsAndShowImagePicker() {
|
||||
// if (ContextCompat.checkSelfPermission(
|
||||
// requireContext(),
|
||||
// Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
// ) != PackageManager.PERMISSION_GRANTED
|
||||
// ) {
|
||||
// ActivityCompat.requestPermissions(
|
||||
// requireActivity(),
|
||||
// arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.CAMERA),
|
||||
// Constants.REQUEST_STORAGE_PERMISSION
|
||||
// )
|
||||
// } else {
|
||||
// showImagePickerOptions()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private fun showImagePickerOptions() {
|
||||
// val options = arrayOf(
|
||||
// getString(R.string.take_photo),
|
||||
// getString(R.string.choose_from_gallery),
|
||||
// getString(R.string.cancel)
|
||||
// )
|
||||
//
|
||||
// androidx.appcompat.app.AlertDialog.Builder(requireContext())
|
||||
// .setTitle(getString(R.string.select_attachment))
|
||||
// .setItems(options) { dialog, which ->
|
||||
// when (which) {
|
||||
// 0 -> openCamera()
|
||||
// 1 -> openGallery()
|
||||
// }
|
||||
// dialog.dismiss()
|
||||
// }
|
||||
// .show()
|
||||
// }
|
||||
//
|
||||
// private fun openCamera() {
|
||||
// val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
|
||||
// val imageFileName = "IMG_${timeStamp}.jpg"
|
||||
// val storageDir = requireContext().getExternalFilesDir(null)
|
||||
// val imageFile = File(storageDir, imageFileName)
|
||||
//
|
||||
// tempImageUri = FileProvider.getUriForFile(
|
||||
// requireContext(),
|
||||
// "${requireContext().packageName}.fileprovider",
|
||||
// imageFile
|
||||
// )
|
||||
//
|
||||
// val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE).apply {
|
||||
// putExtra(MediaStore.EXTRA_OUTPUT, tempImageUri)
|
||||
// }
|
||||
//
|
||||
// takePictureLauncher.launch(intent)
|
||||
// }
|
||||
//
|
||||
// private fun openGallery() {
|
||||
// val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
|
||||
// pickImageLauncher.launch(intent)
|
||||
// }
|
||||
//
|
||||
// private fun handleSelectedImage(uri: Uri) {
|
||||
// // Get the file from Uri
|
||||
// val filePathColumn = arrayOf(MediaStore.Images.Media.DATA)
|
||||
// val cursor = requireContext().contentResolver.query(uri, filePathColumn, null, null, null)
|
||||
// cursor?.moveToFirst()
|
||||
// val columnIndex = cursor?.getColumnIndex(filePathColumn[0])
|
||||
// val filePath = cursor?.getString(columnIndex ?: 0)
|
||||
// cursor?.close()
|
||||
//
|
||||
// if (filePath != null) {
|
||||
// viewModel.setSelectedImageFile(File(filePath))
|
||||
// Toast.makeText(requireContext(), R.string.image_selected, Toast.LENGTH_SHORT).show()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override fun onRequestPermissionsResult(
|
||||
// requestCode: Int,
|
||||
// permissions: Array<out String>,
|
||||
// grantResults: IntArray
|
||||
// ) {
|
||||
// super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
// if (requestCode == Constants.REQUEST_STORAGE_PERMISSION) {
|
||||
// if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
// showImagePickerOptions()
|
||||
// } else {
|
||||
// Toast.makeText(requireContext(), R.string.permission_denied, Toast.LENGTH_SHORT).show()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override fun onDestroyView() {
|
||||
// super.onDestroyView()
|
||||
// typingHandler.removeCallbacks(stopTypingRunnable)
|
||||
// _binding = null
|
||||
// }
|
||||
//}
|
@ -21,6 +21,7 @@ class ChatListFragment : Fragment() {
|
||||
private val binding get() = _binding!!
|
||||
private lateinit var socketService: SocketIOService
|
||||
private lateinit var sessionManager: SessionManager
|
||||
|
||||
private val viewModel: com.alya.ecommerce_serang.ui.chat.ChatViewModel by viewModels {
|
||||
BaseViewModelFactory {
|
||||
val apiService = ApiConfig.getApiService(sessionManager)
|
||||
@ -65,7 +66,8 @@ class ChatListFragment : Fragment() {
|
||||
productImage = null,
|
||||
productRating = null,
|
||||
storeName = chatItem.storeName,
|
||||
chatRoomId = chatItem.chatRoomId
|
||||
chatRoomId = chatItem.chatRoomId,
|
||||
storeImage = chatItem.storeImage
|
||||
)
|
||||
}
|
||||
binding.chatListRecyclerView.adapter = adapter
|
||||
@ -85,4 +87,8 @@ class ChatListFragment : Fragment() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
companion object{
|
||||
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ import androidx.lifecycle.viewModelScope
|
||||
import com.alya.ecommerce_serang.data.api.response.chat.ChatItem
|
||||
import com.alya.ecommerce_serang.data.api.response.chat.ChatItemList
|
||||
import com.alya.ecommerce_serang.data.api.response.chat.ChatLine
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.product.StoreProduct
|
||||
import com.alya.ecommerce_serang.data.repository.ChatRepository
|
||||
import com.alya.ecommerce_serang.data.repository.Result
|
||||
import com.alya.ecommerce_serang.utils.Constants
|
||||
@ -23,6 +24,8 @@ import javax.inject.Inject
|
||||
class ChatViewModel @Inject constructor(
|
||||
private val chatRepository: ChatRepository,
|
||||
private val socketService: SocketIOService,
|
||||
|
||||
|
||||
private val sessionManager: SessionManager
|
||||
) : ViewModel() {
|
||||
|
||||
@ -38,6 +41,9 @@ class ChatViewModel @Inject constructor(
|
||||
private val _chatList = MutableLiveData<Result<List<ChatItemList>>>()
|
||||
val chatList: LiveData<Result<List<ChatItemList>>> = _chatList
|
||||
|
||||
private val _storeDetail = MutableLiveData<Result<StoreProduct?>>()
|
||||
val storeDetail : LiveData<Result<StoreProduct?>> get() = _storeDetail
|
||||
|
||||
// Store and product parameters
|
||||
private var storeId: Int = 0
|
||||
private var productId: Int? = 0
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.alya.ecommerce_serang.ui.order
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
@ -9,6 +10,7 @@ import com.alya.ecommerce_serang.data.api.dto.CourierCostRequest
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.CourierCostsItem
|
||||
import com.alya.ecommerce_serang.data.repository.OrderRepository
|
||||
import com.alya.ecommerce_serang.data.repository.Result
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ShippingViewModel(
|
||||
@ -30,12 +32,71 @@ class ShippingViewModel(
|
||||
/**
|
||||
* Load shipping options based on address, product, and quantity
|
||||
*/
|
||||
// fun loadShippingOptions(addressId: Int, productId: Int, quantity: Int) {
|
||||
// _isLoading.value = true
|
||||
// _errorMessage.value = ""
|
||||
//
|
||||
// val costProduct = CostProduct(
|
||||
// productId = productId,
|
||||
// quantity = quantity
|
||||
// )
|
||||
//
|
||||
// viewModelScope.launch {
|
||||
// // Define the courier services to try
|
||||
// val courierServices = listOf("pos", "jne", "tiki")
|
||||
//
|
||||
// // Create a mutable list to collect successful courier options
|
||||
// val availableCourierOptions = mutableListOf<CourierCostsItem>()
|
||||
//
|
||||
// // Try each courier service
|
||||
// for (courier in courierServices) {
|
||||
// try {
|
||||
// // Create a request for this specific courier
|
||||
// val courierRequest = CourierCostRequest(
|
||||
// addressId = addressId,
|
||||
// itemCost = listOf(costProduct),
|
||||
// courier = courier // Add the courier to the request
|
||||
// )
|
||||
//
|
||||
// // Make a separate API call for each courier
|
||||
// val result = repository.getCountCourierCost(courierRequest)
|
||||
//
|
||||
// when (result) {
|
||||
// is Result.Success -> {
|
||||
// // Add this courier's options to our collection
|
||||
// result.data.courierCosts?.let { costs ->
|
||||
// availableCourierOptions.addAll(costs)
|
||||
// }
|
||||
// // Update UI with what we have so far
|
||||
// _shippingOptions.value = availableCourierOptions
|
||||
// }
|
||||
// is Result.Error -> {
|
||||
// // Log the error but continue with next courier
|
||||
// Log.e("ShippingViewModel", "Error fetching cost for courier $courier: ${result.exception.message}")
|
||||
// }
|
||||
// is Result.Loading -> {
|
||||
// // Handle loading state
|
||||
// }
|
||||
// }
|
||||
// } catch (e: Exception) {
|
||||
// // Log the exception but continue with next courier
|
||||
// Log.e("ShippingViewModel", "Exception for courier $courier: ${e.message}")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Show error only if we couldn't get any shipping options
|
||||
// if (availableCourierOptions.isEmpty()) {
|
||||
// _errorMessage.value = "No shipping options available. Please try again later."
|
||||
// }
|
||||
//
|
||||
// _isLoading.value = false
|
||||
// }
|
||||
// }
|
||||
|
||||
fun loadShippingOptions(addressId: Int, productId: Int, quantity: Int) {
|
||||
// Reset previous state
|
||||
_isLoading.value = true
|
||||
_errorMessage.value = ""
|
||||
|
||||
// Prepare the request
|
||||
val costProduct = CostProduct(
|
||||
productId = productId,
|
||||
quantity = quantity
|
||||
@ -43,34 +104,47 @@ class ShippingViewModel(
|
||||
|
||||
val request = CourierCostRequest(
|
||||
addressId = addressId,
|
||||
itemCost = listOf(costProduct) // Wrap in a list
|
||||
itemCost = listOf(costProduct)
|
||||
)
|
||||
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
// Fetch courier costs
|
||||
val result = repository.getCountCourierCost(request)
|
||||
var success = false
|
||||
var attempt = 0
|
||||
val maxAttempts = 3
|
||||
|
||||
when (result) {
|
||||
is Result.Success -> {
|
||||
// Update shipping options directly with courier costs
|
||||
_shippingOptions.value = result.data.courierCosts
|
||||
}
|
||||
is Result.Error -> {
|
||||
// Handle error case
|
||||
_errorMessage.value = result.exception.message ?: "Unknown error occurred"
|
||||
}
|
||||
is Result.Loading -> {
|
||||
// Typically handled by the loading state
|
||||
while (!success && attempt < maxAttempts) {
|
||||
attempt++
|
||||
|
||||
try {
|
||||
val result = repository.getCountCourierCost(request)
|
||||
|
||||
when (result) {
|
||||
is Result.Success -> {
|
||||
_shippingOptions.value = result.data.courierCosts
|
||||
success = true
|
||||
}
|
||||
is com.alya.ecommerce_serang.data.repository.Result.Error -> {
|
||||
Log.e("ShippingViewModel", "Attempt $attempt failed: ${result.exception.message}")
|
||||
// Wait before retrying
|
||||
delay(120000)
|
||||
}
|
||||
is com.alya.ecommerce_serang.data.repository.Result.Loading -> {
|
||||
// Handle loading state
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("ShippingViewModel", "Attempt $attempt exception: ${e.message}")
|
||||
// Wait before retrying
|
||||
delay(1000)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// Catch any unexpected exceptions
|
||||
_errorMessage.value = e.localizedMessage ?: "An unexpected error occurred"
|
||||
} finally {
|
||||
// Always set loading to false
|
||||
_isLoading.value = false
|
||||
}
|
||||
|
||||
// After all attempts, check if we have any shipping options
|
||||
if (!success || _shippingOptions.value.isNullOrEmpty()) {
|
||||
_errorMessage.value = "No shipping options available. Please try again later."
|
||||
}
|
||||
|
||||
_isLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
@ -398,7 +398,7 @@ class AddAddressActivity : AppCompatActivity() {
|
||||
isRequestingLocation = false
|
||||
Toast.makeText(this, "Timeout lokasi, menggunakan lokasi default", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}, 15000) // 15 seconds timeout
|
||||
}, 60000) // 15 seconds timeout
|
||||
|
||||
// Try getting last known location first
|
||||
try {
|
||||
|
@ -5,8 +5,10 @@ import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.alya.ecommerce_serang.data.api.dto.CancelOrderReq
|
||||
import com.alya.ecommerce_serang.data.api.dto.CompletedOrderRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.OrdersItem
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.CancelOrderResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.OrderListItemsItem
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.Orders
|
||||
import com.alya.ecommerce_serang.data.api.response.order.CompletedOrderResponse
|
||||
@ -31,6 +33,11 @@ class HistoryViewModel(private val repository: OrderRepository) : ViewModel() {
|
||||
private val _orderDetails = MutableLiveData<Orders>()
|
||||
val orderDetails: LiveData<Orders> get() = _orderDetails
|
||||
|
||||
private val _cancelOrderStatus = MutableLiveData<Result<CancelOrderResponse>>()
|
||||
val cancelOrderStatus: LiveData<Result<CancelOrderResponse>> = _cancelOrderStatus
|
||||
private val _isCancellingOrder = MutableLiveData<Boolean>()
|
||||
val isCancellingOrder: LiveData<Boolean> = _isCancellingOrder
|
||||
|
||||
// LiveData untuk OrderItems
|
||||
private val _orderItems = MutableLiveData<List<OrderListItemsItem>>()
|
||||
val orderItems: LiveData<List<OrderListItemsItem>> get() = _orderItems
|
||||
@ -131,4 +138,26 @@ class HistoryViewModel(private val repository: OrderRepository) : ViewModel() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun cancelOrder(cancelReq: CancelOrderReq) {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
_cancelOrderStatus.value = Result.Loading
|
||||
val result = repository.cancelOrder(cancelReq)
|
||||
_cancelOrderStatus.value = result
|
||||
} catch (e: Exception) {
|
||||
Log.e("HistoryViewModel", "Error cancelling order: ${e.message}")
|
||||
_cancelOrderStatus.value = Result.Error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun refreshOrders(status: String = "all") {
|
||||
Log.d(TAG, "Refreshing orders with status: $status")
|
||||
// Clear current orders before fetching new ones
|
||||
_orders.value = ViewState.Loading
|
||||
|
||||
// Re-fetch the orders with the current status
|
||||
getOrderList(status)
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package com.alya.ecommerce_serang.ui.order.history
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.ContextWrapper
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
@ -17,6 +18,7 @@ import android.widget.ImageView
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.findViewTreeLifecycleOwner
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
@ -24,6 +26,7 @@ import com.alya.ecommerce_serang.R
|
||||
import com.alya.ecommerce_serang.data.api.dto.OrdersItem
|
||||
import com.alya.ecommerce_serang.data.api.dto.ReviewUIItem
|
||||
import com.alya.ecommerce_serang.ui.order.detail.PaymentActivity
|
||||
import com.alya.ecommerce_serang.ui.order.history.cancelorder.CancelOrderBottomSheet
|
||||
import com.alya.ecommerce_serang.ui.order.review.CreateReviewActivity
|
||||
import com.alya.ecommerce_serang.ui.product.ReviewProductActivity
|
||||
import com.google.android.material.button.MaterialButton
|
||||
@ -150,7 +153,8 @@ class OrderHistoryAdapter(
|
||||
visibility = View.VISIBLE
|
||||
text = itemView.context.getString(R.string.canceled_order_btn)
|
||||
setOnClickListener {
|
||||
showCancelOrderDialog(order.orderId.toString())
|
||||
showCancelOrderBottomSheet(order.orderId)
|
||||
viewModel.refreshOrders()
|
||||
}
|
||||
}
|
||||
deadlineDate.apply {
|
||||
@ -171,7 +175,8 @@ class OrderHistoryAdapter(
|
||||
visibility = View.VISIBLE
|
||||
text = itemView.context.getString(R.string.canceled_order_btn)
|
||||
setOnClickListener {
|
||||
showCancelOrderDialog(order.orderId.toString())
|
||||
showCancelOrderBottomSheet(order.orderId)
|
||||
viewModel.refreshOrders()
|
||||
}
|
||||
}
|
||||
|
||||
@ -208,6 +213,7 @@ class OrderHistoryAdapter(
|
||||
text = itemView.context.getString(R.string.canceled_order_btn)
|
||||
setOnClickListener {
|
||||
showCancelOrderDialog(order.orderId.toString())
|
||||
viewModel.refreshOrders()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -226,7 +232,7 @@ class OrderHistoryAdapter(
|
||||
text = itemView.context.getString(R.string.claim_complaint)
|
||||
setOnClickListener {
|
||||
showCancelOrderDialog(order.orderId.toString())
|
||||
// Handle click event
|
||||
viewModel.refreshOrders()
|
||||
}
|
||||
}
|
||||
btnRight.apply {
|
||||
@ -235,6 +241,7 @@ class OrderHistoryAdapter(
|
||||
setOnClickListener {
|
||||
// Handle click event
|
||||
viewModel.confirmOrderCompleted(order.orderId, "completed")
|
||||
viewModel.refreshOrders()
|
||||
|
||||
}
|
||||
}
|
||||
@ -243,16 +250,6 @@ class OrderHistoryAdapter(
|
||||
text = formatShipmentDate(order.updatedAt, order.etd ?: "0")
|
||||
}
|
||||
}
|
||||
"delivered" -> {
|
||||
// Untuk status delivered, tampilkan "Beri Ulasan"
|
||||
btnRight.apply {
|
||||
visibility = View.VISIBLE
|
||||
text = itemView.context.getString(R.string.add_review)
|
||||
setOnClickListener {
|
||||
// Handle click event
|
||||
}
|
||||
}
|
||||
}
|
||||
"completed" -> {
|
||||
statusOrder.apply {
|
||||
visibility = View.VISIBLE
|
||||
@ -267,6 +264,7 @@ class OrderHistoryAdapter(
|
||||
text = itemView.context.getString(R.string.add_review)
|
||||
setOnClickListener {
|
||||
addReviewProduct(order)
|
||||
viewModel.refreshOrders()
|
||||
// Handle click event
|
||||
}
|
||||
}
|
||||
@ -492,6 +490,48 @@ class OrderHistoryAdapter(
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
private fun showCancelOrderBottomSheet(orderId : Int) {
|
||||
val context = itemView.context
|
||||
|
||||
// We need a FragmentManager to show the bottom sheet
|
||||
// Try to get it from the context
|
||||
val fragmentActivity = when (context) {
|
||||
is FragmentActivity -> context
|
||||
is ContextWrapper -> {
|
||||
val baseContext = context.baseContext
|
||||
if (baseContext is FragmentActivity) {
|
||||
baseContext
|
||||
} else {
|
||||
// Log error and show a Toast instead if we can't get a FragmentManager
|
||||
Log.e("OrderHistoryAdapter", "Cannot show bottom sheet: Context is not a FragmentActivity")
|
||||
Toast.makeText(context, "Cannot show cancel order dialog", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// Log error and show a Toast instead if we can't get a FragmentManager
|
||||
Log.e("OrderHistoryAdapter", "Cannot show bottom sheet: Context is not a FragmentActivity")
|
||||
Toast.makeText(context, "Cannot show cancel order dialog", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Create and show the bottom sheet using the obtained FragmentManager
|
||||
val bottomSheet = CancelOrderBottomSheet(
|
||||
orderId = orderId,
|
||||
onOrderCancelled = {
|
||||
// Handle the successful cancellation
|
||||
// Refresh the data
|
||||
viewModel.refreshOrders() // Assuming there's a method to refresh orders
|
||||
|
||||
// Show a success message
|
||||
Toast.makeText(context, "Order cancelled successfully", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
)
|
||||
|
||||
bottomSheet.show(fragmentActivity.supportFragmentManager, CancelOrderBottomSheet.TAG)
|
||||
}
|
||||
|
||||
private fun addReviewProduct(order: OrdersItem) {
|
||||
// Use ViewModel to fetch order details
|
||||
viewModel.getOrderDetails(order.orderId)
|
||||
|
@ -47,11 +47,9 @@ class OrderHistoryFragment : Fragment() {
|
||||
1 -> getString(R.string.pending_orders)
|
||||
2 -> getString(R.string.unpaid_orders)
|
||||
3 -> getString(R.string.processed_orders)
|
||||
4 -> getString(R.string.paid_orders)
|
||||
5 -> getString(R.string.shipped_orders)
|
||||
6 -> getString(R.string.delivered_orders)
|
||||
7 -> getString(R.string.completed_orders)
|
||||
8 -> getString(R.string.canceled_orders)
|
||||
4 -> getString(R.string.shipped_orders)
|
||||
5 -> getString(R.string.completed_orders)
|
||||
6 -> getString(R.string.canceled_orders)
|
||||
else -> "Tab $position"
|
||||
}
|
||||
}.attach()
|
||||
|
@ -14,9 +14,7 @@ class OrderViewPagerAdapter(
|
||||
"pending", // Menunggu Tagihan
|
||||
"unpaid", // Belum Dibayar
|
||||
"processed", // Diproses
|
||||
"paid", // Dibayar
|
||||
"shipped", // Dikirim
|
||||
"delivered", // Diterima
|
||||
"completed", // Selesai
|
||||
"canceled" // Dibatalkan
|
||||
)
|
||||
|
@ -0,0 +1,173 @@
|
||||
package com.alya.ecommerce_serang.ui.order.history.cancelorder
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AdapterView
|
||||
import android.widget.Button
|
||||
import android.widget.Spinner
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.alya.ecommerce_serang.R
|
||||
import com.alya.ecommerce_serang.data.api.dto.CancelOrderReq
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||
import com.alya.ecommerce_serang.data.repository.OrderRepository
|
||||
import com.alya.ecommerce_serang.data.repository.Result
|
||||
import com.alya.ecommerce_serang.ui.order.history.HistoryViewModel
|
||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||
import com.alya.ecommerce_serang.utils.SessionManager
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
|
||||
class CancelOrderBottomSheet(
|
||||
private val orderId: Int,
|
||||
private val onOrderCancelled: () -> Unit
|
||||
) : BottomSheetDialogFragment() {
|
||||
private lateinit var sessionManager: SessionManager
|
||||
|
||||
private val viewModel: HistoryViewModel by viewModels {
|
||||
BaseViewModelFactory {
|
||||
val apiService = ApiConfig.getApiService(sessionManager)
|
||||
val orderRepository = OrderRepository(apiService)
|
||||
HistoryViewModel(orderRepository)
|
||||
}
|
||||
}
|
||||
private var selectedReason: CancelOrderReq? = null
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.layout_cancel_order_bottom, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
sessionManager = SessionManager(requireContext())
|
||||
|
||||
val tvTitle = view.findViewById<TextView>(R.id.tv_title)
|
||||
val spinnerReason = view.findViewById<Spinner>(R.id.spinner_reason)
|
||||
val btnCancel = view.findViewById<Button>(R.id.btn_cancel)
|
||||
val btnConfirm = view.findViewById<Button>(R.id.btn_confirm)
|
||||
|
||||
// Set the title
|
||||
tvTitle.text = "Cancel Order #$orderId"
|
||||
|
||||
// Set up the spinner with cancellation reasons
|
||||
setupReasonSpinner(spinnerReason)
|
||||
|
||||
// Handle button clicks
|
||||
btnCancel.setOnClickListener {
|
||||
dismiss()
|
||||
}
|
||||
|
||||
btnConfirm.setOnClickListener {
|
||||
if (selectedReason == null) {
|
||||
Toast.makeText(context, "Please select a reason", Toast.LENGTH_SHORT).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
cancelOrder()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupReasonSpinner(spinner: Spinner) {
|
||||
val reasons = getCancellationReasons()
|
||||
val adapter = CancelReasonAdapter(requireContext(), reasons)
|
||||
spinner.adapter = adapter
|
||||
|
||||
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||
selectedReason = reasons[position]
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>?) {
|
||||
selectedReason = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCancellationReasons(): List<CancelOrderReq> {
|
||||
// These should ideally come from the server or a configuration
|
||||
return listOf(
|
||||
CancelOrderReq(1, "Changed my mind"),
|
||||
CancelOrderReq(2, "Found a better option"),
|
||||
CancelOrderReq(3, "Ordered by mistake"),
|
||||
CancelOrderReq(4, "Delivery time too long"),
|
||||
CancelOrderReq(5, "Other reason")
|
||||
)
|
||||
}
|
||||
|
||||
private fun cancelOrder() {
|
||||
// Validate reason selection
|
||||
if (selectedReason == null) {
|
||||
Toast.makeText(context, "Mohon pilih alasan pembatalan", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
// Create cancel request
|
||||
val cancelRequest = CancelOrderReq(
|
||||
orderId = orderId,
|
||||
reason = selectedReason!!.reason
|
||||
)
|
||||
Log.d(TAG, "Sending cancel request to ViewModel: orderId=${cancelRequest.orderId}, reason='${cancelRequest.reason}'")
|
||||
|
||||
|
||||
// Submit the cancellation
|
||||
viewModel.cancelOrder(cancelRequest)
|
||||
|
||||
// Observe the status
|
||||
viewModel.cancelOrderStatus.observe(viewLifecycleOwner) { result ->
|
||||
when (result) {
|
||||
is Result.Loading -> {
|
||||
// Show loading indicator
|
||||
// showLoading(true)
|
||||
}
|
||||
is Result.Success -> {
|
||||
// Hide loading indicator
|
||||
showLoading(false)
|
||||
|
||||
// Show success message
|
||||
Toast.makeText(
|
||||
context,
|
||||
"Pesanan berhasil dibatalkan",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
Log.d(TAG, "Cancel order status: SUCCESS, message: ${result.data.message}")
|
||||
|
||||
// Notify callback and close dialog
|
||||
onOrderCancelled()
|
||||
dismiss()
|
||||
}
|
||||
is Result.Error -> {
|
||||
// Hide loading indicator
|
||||
showLoading(false)
|
||||
Log.e(TAG, "Cancel order status: ERROR", result.exception)
|
||||
|
||||
|
||||
// Show error message
|
||||
val errorMsg = result.exception.message ?: "Gagal membatalkan pesanan"
|
||||
Toast.makeText(context, errorMsg, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// private fun showLoading(isLoading: Boolean) {
|
||||
// binding.progressBar.isVisible = isLoading
|
||||
// binding.btnCancel.isEnabled = !isLoading
|
||||
// binding.btnConfirm.isEnabled = !isLoading
|
||||
// }
|
||||
|
||||
private fun showLoading(isLoading: Boolean) {
|
||||
// Implement loading indicator if needed
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "CancelOrderBottomSheet"
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package com.alya.ecommerce_serang.ui.order.history.cancelorder
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.TextView
|
||||
import com.alya.ecommerce_serang.R
|
||||
import com.alya.ecommerce_serang.data.api.dto.CancelOrderReq
|
||||
|
||||
class CancelReasonAdapter(
|
||||
context: Context,
|
||||
private val reasons: List<CancelOrderReq>
|
||||
) : ArrayAdapter<CancelOrderReq>(context, 0, reasons) {
|
||||
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
return createItemView(position, convertView, parent)
|
||||
}
|
||||
|
||||
override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
return createItemView(position, convertView, parent)
|
||||
}
|
||||
|
||||
private fun createItemView(position: Int, recycledView: View?, parent: ViewGroup): View {
|
||||
val reason = getItem(position) ?: return recycledView ?: View(context)
|
||||
|
||||
val view = recycledView ?: LayoutInflater.from(context)
|
||||
.inflate(R.layout.item_cancel_order, parent, false)
|
||||
|
||||
val tvReason = view.findViewById<TextView>(R.id.tv_reason)
|
||||
tvReason.text = reason.reason
|
||||
|
||||
return view
|
||||
}
|
||||
}
|
@ -33,6 +33,7 @@ import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||
import com.alya.ecommerce_serang.data.repository.OrderRepository
|
||||
import com.alya.ecommerce_serang.databinding.ActivityDetailOrderStatusBinding
|
||||
import com.alya.ecommerce_serang.ui.order.detail.PaymentActivity
|
||||
import com.alya.ecommerce_serang.ui.order.history.cancelorder.CancelOrderBottomSheet
|
||||
import com.alya.ecommerce_serang.ui.order.review.CreateReviewActivity
|
||||
import com.alya.ecommerce_serang.ui.product.ReviewProductActivity
|
||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||
@ -50,6 +51,7 @@ class DetailOrderStatusActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var binding: ActivityDetailOrderStatusBinding
|
||||
private lateinit var sessionManager: SessionManager
|
||||
|
||||
private var orderId: Int = -1
|
||||
private var orderStatus: String = ""
|
||||
private val orders = mutableListOf<OrdersItem>()
|
||||
@ -181,9 +183,9 @@ class DetailOrderStatusActivity : AppCompatActivity() {
|
||||
setupProductsRecyclerView(orders.orderItems)
|
||||
|
||||
// Set payment method
|
||||
binding.tvPaymentMethod.text = "Bank Transfer - ${orders.payInfoName ?: "BCA"}"
|
||||
binding.tvPaymentMethod.text = "Bank Transfer - ${orders.payInfoName ?: "Tidak tersedia"}"
|
||||
|
||||
Log.d(TAG, "populateOrderDetails: Payment method=${orders.payInfoName ?: "BCA"}")
|
||||
Log.d(TAG, "populateOrderDetails: Payment method=${orders.payInfoName ?: "Tidak tersedia"}")
|
||||
|
||||
// Set subtotal, shipping cost, and total
|
||||
val subtotal = orders.totalAmount?.toIntOrNull()?.minus(orders.shipmentPrice.toIntOrNull() ?: 0) ?: 0
|
||||
@ -237,10 +239,8 @@ class DetailOrderStatusActivity : AppCompatActivity() {
|
||||
Log.d(TAG, "adjustButtonsBasedOnStatus: Status header set to '$statusText'")
|
||||
|
||||
when (status) {
|
||||
"pending", "unpaid" -> {
|
||||
Log.d(TAG, "adjustButtonsBasedOnStatus: Setting up UI for pending/unpaid order")
|
||||
|
||||
// Show status note
|
||||
"pending"->{
|
||||
binding.tvStatusHeader.text = "Menunggu Tagihan"
|
||||
binding.tvStatusNote.visibility = View.VISIBLE
|
||||
binding.tvStatusNote.text = "Pesanan ini harus dibayar sebelum ${formatDatePay(orders.updatedAt)}"
|
||||
|
||||
@ -250,7 +250,27 @@ class DetailOrderStatusActivity : AppCompatActivity() {
|
||||
text = "Batalkan Pesanan"
|
||||
setOnClickListener {
|
||||
Log.d(TAG, "Cancel Order button clicked")
|
||||
showCancelOrderDialog(orders.orderId.toString())
|
||||
showCancelOrderBottomSheet(orders.orderId)
|
||||
viewModel.getOrderDetails(orders.orderId)
|
||||
}
|
||||
}
|
||||
}
|
||||
"unpaid" -> {
|
||||
Log.d(TAG, "adjustButtonsBasedOnStatus: Setting up UI for pending/unpaid order")
|
||||
|
||||
// Show status note
|
||||
binding.tvStatusHeader.text = "Belum Dibayar"
|
||||
binding.tvStatusNote.visibility = View.VISIBLE
|
||||
binding.tvStatusNote.text = "Pesanan ini harus dibayar sebelum ${formatDatePay(orders.updatedAt)}"
|
||||
|
||||
// Set buttons
|
||||
binding.btnSecondary.apply {
|
||||
visibility = View.VISIBLE
|
||||
text = "Batalkan Pesanan"
|
||||
setOnClickListener {
|
||||
Log.d(TAG, "Cancel Order button clicked")
|
||||
showCancelOrderBottomSheet(orders.orderId)
|
||||
viewModel.getOrderDetails(orders.orderId)
|
||||
}
|
||||
}
|
||||
|
||||
@ -270,6 +290,7 @@ class DetailOrderStatusActivity : AppCompatActivity() {
|
||||
"processed" -> {
|
||||
Log.d(TAG, "adjustButtonsBasedOnStatus: Setting up UI for processed order")
|
||||
|
||||
binding.tvStatusHeader.text = "Sedang Diproses"
|
||||
binding.tvStatusNote.visibility = View.VISIBLE
|
||||
binding.tvStatusNote.text = "Penjual sedang memproses pesanan Anda"
|
||||
|
||||
@ -279,13 +300,19 @@ class DetailOrderStatusActivity : AppCompatActivity() {
|
||||
setOnClickListener {
|
||||
Log.d(TAG, "Cancel Order button clicked for processed order")
|
||||
showCancelOrderDialog(orders.orderId.toString())
|
||||
viewModel.getOrderDetails(orders.orderId)
|
||||
}
|
||||
}
|
||||
|
||||
binding.btnPrimary.apply {
|
||||
visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
"shipped" -> {
|
||||
Log.d(TAG, "adjustButtonsBasedOnStatus: Setting up UI for shipped order")
|
||||
|
||||
binding.tvStatusHeader.text = "Sudah Dikirim"
|
||||
binding.tvStatusNote.visibility = View.VISIBLE
|
||||
binding.tvStatusNote.text = "Pesanan Anda sedang dalam perjalanan. Akan sampai sekitar ${formatShipmentDate(orders.updatedAt, orders.etd ?: "0")}"
|
||||
|
||||
@ -294,7 +321,8 @@ class DetailOrderStatusActivity : AppCompatActivity() {
|
||||
text = "Ajukan Komplain"
|
||||
setOnClickListener {
|
||||
Log.d(TAG, "Complaint button clicked")
|
||||
showCancelOrderDialog(orders.orderId.toString()) // For now, reuse the cancel dialog
|
||||
showCancelOrderDialog(orders.orderId.toString())
|
||||
viewModel.getOrderDetails(orders.orderId)
|
||||
}
|
||||
}
|
||||
|
||||
@ -314,11 +342,11 @@ class DetailOrderStatusActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
"delivered", "completed" -> {
|
||||
"completed" -> {
|
||||
Log.d(TAG, "adjustButtonsBasedOnStatus: Setting up UI for delivered/completed order")
|
||||
|
||||
binding.tvStatusNote.visibility = View.VISIBLE
|
||||
binding.tvStatusNote.text = "Pesanan telah selesai"
|
||||
binding.tvStatusHeader.text = "Pesanan Selesai"
|
||||
binding.tvStatusNote.visibility = View.GONE
|
||||
|
||||
binding.btnPrimary.apply {
|
||||
visibility = View.VISIBLE
|
||||
@ -326,15 +354,27 @@ class DetailOrderStatusActivity : AppCompatActivity() {
|
||||
setOnClickListener {
|
||||
Log.d(TAG, "Review button clicked")
|
||||
addReviewForOrder(orders)
|
||||
viewModel.getOrderDetails(orders.orderId)
|
||||
}
|
||||
}
|
||||
binding.btnSecondary.apply {
|
||||
visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
"canceled" -> {
|
||||
Log.d(TAG, "adjustButtonsBasedOnStatus: Setting up UI for canceled order")
|
||||
|
||||
binding.tvStatusHeader.text = "Pesanan Selesai"
|
||||
binding.tvStatusNote.visibility = View.VISIBLE
|
||||
binding.tvStatusNote.text = "Pesanan dibatalkan: ${orders.cancelReason ?: "Alasan tidak diberikan"}"
|
||||
|
||||
binding.btnSecondary.apply {
|
||||
visibility = View.GONE
|
||||
}
|
||||
binding.btnPrimary.apply {
|
||||
visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -530,6 +570,22 @@ class DetailOrderStatusActivity : AppCompatActivity() {
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
private fun showCancelOrderBottomSheet(orderId: Int) {
|
||||
// Create and show the bottom sheet directly since we're already in an Activity
|
||||
val bottomSheet = CancelOrderBottomSheet(
|
||||
orderId = orderId,
|
||||
onOrderCancelled = {
|
||||
// Handle the successful cancellation
|
||||
// Refresh the data
|
||||
|
||||
// Show a success message
|
||||
Toast.makeText(this, "Order cancelled successfully", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
)
|
||||
|
||||
bottomSheet.show(supportFragmentManager, CancelOrderBottomSheet.TAG)
|
||||
}
|
||||
|
||||
private fun formatDate(dateString: String): String {
|
||||
Log.d(TAG, "formatDate: Formatting date: $dateString")
|
||||
|
||||
|
@ -414,7 +414,8 @@ class DetailProductActivity : AppCompatActivity() {
|
||||
productImage = productDetail.image,
|
||||
productRating = productDetail.rating,
|
||||
storeName = storeDetail.data.storeName,
|
||||
chatRoomId = 0
|
||||
chatRoomId = 0,
|
||||
storeImage = storeDetail.data.storeImage
|
||||
)
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package com.alya.ecommerce_serang.ui.profile
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.app.ProgressDialog
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
@ -9,18 +11,24 @@ import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.alya.ecommerce_serang.BuildConfig.BASE_URL
|
||||
import com.alya.ecommerce_serang.R
|
||||
import com.alya.ecommerce_serang.data.api.dto.UserProfile
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||
import com.alya.ecommerce_serang.data.repository.UserRepository
|
||||
import com.alya.ecommerce_serang.databinding.FragmentProfileBinding
|
||||
import com.alya.ecommerce_serang.ui.auth.LoginActivity
|
||||
import com.alya.ecommerce_serang.ui.auth.RegisterStoreActivity
|
||||
import com.alya.ecommerce_serang.ui.order.address.AddressActivity
|
||||
import com.alya.ecommerce_serang.ui.order.history.HistoryActivity
|
||||
import com.alya.ecommerce_serang.ui.profile.mystore.MyStoreActivity
|
||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||
import com.alya.ecommerce_serang.utils.SessionManager
|
||||
import com.alya.ecommerce_serang.utils.viewmodel.ProfileViewModel
|
||||
import com.bumptech.glide.Glide
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ProfileFragment : Fragment() {
|
||||
|
||||
@ -53,10 +61,21 @@ class ProfileFragment : Fragment() {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
observeUserProfile()
|
||||
viewModel.loadUserProfile()
|
||||
viewModel.checkStoreUser()
|
||||
|
||||
binding.cardBukaToko.setOnClickListener{
|
||||
val intentBuka = Intent(requireContext(), MyStoreActivity::class.java)
|
||||
startActivity(intentBuka)
|
||||
val hasStore = viewModel.checkStore.value
|
||||
// val hasStore = false
|
||||
|
||||
Log.d("Profile Fragment", "Check store $hasStore")
|
||||
|
||||
if (hasStore == true){
|
||||
val intentBuka = Intent(requireContext(), MyStoreActivity::class.java)
|
||||
startActivity(intentBuka)
|
||||
} else {
|
||||
val intentBuka = Intent(requireContext(), RegisterStoreActivity::class.java)
|
||||
startActivity(intentBuka)
|
||||
}
|
||||
}
|
||||
|
||||
binding.btnDetailProfile.setOnClickListener{
|
||||
@ -73,6 +92,16 @@ class ProfileFragment : Fragment() {
|
||||
val intent = Intent(requireContext(), HistoryActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
binding.cardLogout.setOnClickListener({
|
||||
logout()
|
||||
|
||||
})
|
||||
|
||||
binding.cardAddress.setOnClickListener({
|
||||
val intent = Intent(requireContext(), AddressActivity::class.java)
|
||||
startActivity(intent)
|
||||
})
|
||||
}
|
||||
|
||||
private fun observeUserProfile() {
|
||||
@ -103,4 +132,41 @@ class ProfileFragment : Fragment() {
|
||||
.placeholder(R.drawable.placeholder_image)
|
||||
.into(profileImage)
|
||||
}
|
||||
|
||||
private fun logout(){
|
||||
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setTitle("Konfirmasi")
|
||||
.setMessage("Apakah anda yakin ingin keluar?")
|
||||
.setPositiveButton("Ya") { _, _ ->
|
||||
actionLogout()
|
||||
}
|
||||
.setNegativeButton("Tidak", null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun actionLogout(){
|
||||
val loadingDialog = ProgressDialog(requireContext()).apply {
|
||||
setMessage("Mohon ditunggu")
|
||||
setCancelable(false)
|
||||
show()
|
||||
}
|
||||
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
delay(500)
|
||||
loadingDialog.dismiss()
|
||||
sessionManager.clearAll()
|
||||
val intent = Intent(requireContext(), LoginActivity::class.java)
|
||||
startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
"Gagal keluar: ${e.message}",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -20,6 +20,7 @@ object Constants {
|
||||
const val EXTRA_PRODUCT_PRICE = "product_price"
|
||||
const val EXTRA_PRODUCT_IMAGE = "product_image"
|
||||
const val EXTRA_PRODUCT_RATING = "product_rating"
|
||||
const val EXTRA_STORE_IMAGE = "store_image"
|
||||
|
||||
// Request codes
|
||||
const val REQUEST_IMAGE_PICK = 1001
|
||||
|
@ -57,8 +57,6 @@ class SessionManager(context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//clear data when log out
|
||||
fun clearAll() {
|
||||
sharedPreferences.edit() {
|
||||
|
@ -1,9 +1,12 @@
|
||||
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.FcmReq
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.FcmTokenResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.LoginResponse
|
||||
import com.alya.ecommerce_serang.data.repository.Result
|
||||
import com.alya.ecommerce_serang.data.repository.UserRepository
|
||||
@ -13,6 +16,13 @@ class LoginViewModel(private val repository: UserRepository) : ViewModel() {
|
||||
private val _loginState = MutableLiveData<Result<LoginResponse>>()
|
||||
val loginState: LiveData<Result<LoginResponse>> get() = _loginState
|
||||
|
||||
private val _otpState = MutableLiveData<Result<Unit>>()
|
||||
val otpState: LiveData<Result<Unit>> = _otpState
|
||||
|
||||
// MutableLiveData to store messages from API responses
|
||||
private val _message = MutableLiveData<String>()
|
||||
val message: LiveData<String> = _message
|
||||
|
||||
fun login(email: String, password: String) {
|
||||
viewModelScope.launch {
|
||||
_loginState.value = Result.Loading
|
||||
@ -20,4 +30,31 @@ class LoginViewModel(private val repository: UserRepository) : ViewModel() {
|
||||
_loginState.value = result
|
||||
}
|
||||
}
|
||||
|
||||
fun sendFcm(token: FcmReq) {
|
||||
viewModelScope.launch {
|
||||
_otpState.value = Result.Loading // Indicating API call in progress
|
||||
|
||||
try {
|
||||
// Call the repository function to request OTP
|
||||
val response: FcmTokenResponse = repository.sendFcm(token)
|
||||
|
||||
// Log and store success message
|
||||
Log.d("RegisterViewModel", "OTP Response: ${response.message}")
|
||||
_message.value = response.message ?: "berhasil" // Store the message for UI feedback
|
||||
|
||||
// Update state to indicate success
|
||||
_otpState.value = Result.Success(Unit)
|
||||
|
||||
} catch (exception: Exception) {
|
||||
// Handle any errors and update state
|
||||
_otpState.value = Result.Error(exception)
|
||||
_message.value = exception.localizedMessage ?: "Failed to request OTP"
|
||||
|
||||
// Log the error for debugging
|
||||
Log.e("RegisterViewModel", "OTP request failed for: $token", exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -8,6 +8,7 @@ import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.alya.ecommerce_serang.data.api.dto.UserProfile
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.HasStoreResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.profile.EditProfileResponse
|
||||
import com.alya.ecommerce_serang.data.repository.Result
|
||||
import com.alya.ecommerce_serang.data.repository.UserRepository
|
||||
@ -23,6 +24,12 @@ class ProfileViewModel(private val userRepository: UserRepository) : ViewModel()
|
||||
private val _editProfileResult = MutableLiveData<Result<EditProfileResponse>>()
|
||||
val editProfileResult: LiveData<Result<EditProfileResponse>> = _editProfileResult
|
||||
|
||||
private val _checkStore = MutableLiveData<Boolean>()
|
||||
val checkStore: LiveData<Boolean> = _checkStore
|
||||
|
||||
private val _logout = MutableLiveData<Boolean>()
|
||||
val logout : LiveData<Boolean> = _checkStore
|
||||
|
||||
fun loadUserProfile(){
|
||||
viewModelScope.launch {
|
||||
when (val result = userRepository.fetchUserProfile()){
|
||||
@ -33,6 +40,28 @@ class ProfileViewModel(private val userRepository: UserRepository) : ViewModel()
|
||||
}
|
||||
}
|
||||
|
||||
fun checkStoreUser(){
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
// Call the repository function to request OTP
|
||||
val response: HasStoreResponse = userRepository.checkStore()
|
||||
|
||||
// Log and store success message
|
||||
Log.d("RegisterViewModel", "OTP Response: ${response.hasStore}")
|
||||
_checkStore.value = response.hasStore // Store the message for UI feedback
|
||||
|
||||
} catch (exception: Exception) {
|
||||
// Handle any errors and update state
|
||||
_checkStore.value = false
|
||||
|
||||
// Log the error for debugging
|
||||
Log.e("RegisterViewModel", "Error:", exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun editProfileDirect(
|
||||
context: Context,
|
||||
username: String,
|
||||
@ -71,6 +100,17 @@ class ProfileViewModel(private val userRepository: UserRepository) : ViewModel()
|
||||
}
|
||||
}
|
||||
|
||||
fun logout(){
|
||||
viewModelScope.launch {
|
||||
try{
|
||||
|
||||
} catch (e: Exception){
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
private const val TAG = "ProfileViewModel"
|
||||
}
|
||||
|
@ -6,7 +6,9 @@ import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.alya.ecommerce_serang.data.api.dto.RegisterRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.VerifRegisReq
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.OtpResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.VerifRegisterResponse
|
||||
import com.alya.ecommerce_serang.data.repository.Result
|
||||
import com.alya.ecommerce_serang.data.repository.UserRepository
|
||||
import kotlinx.coroutines.launch
|
||||
@ -21,6 +23,9 @@ class RegisterViewModel(private val repository: UserRepository) : ViewModel() {
|
||||
private val _otpState = MutableLiveData<Result<Unit>>()
|
||||
val otpState: LiveData<Result<Unit>> = _otpState
|
||||
|
||||
private val _checkValue = MutableLiveData<Result<Boolean>>()
|
||||
val checkValue: LiveData<Result<Boolean>> = _checkValue
|
||||
|
||||
// MutableLiveData to store messages from API responses
|
||||
private val _message = MutableLiveData<String>()
|
||||
val message: LiveData<String> = _message
|
||||
@ -86,4 +91,24 @@ class RegisterViewModel(private val repository: UserRepository) : ViewModel() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun checkValueReg(request: VerifRegisReq){
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
// Call the repository function to request OTP
|
||||
val response: VerifRegisterResponse = repository.checkValue(request)
|
||||
|
||||
// Log and store success message
|
||||
Log.d("RegisterViewModel", "OTP Response: ${response.available}")
|
||||
_checkValue.value = Result.Success(response.available)// Store the message for UI feedback
|
||||
|
||||
} catch (exception: Exception) {
|
||||
// Handle any errors and update state
|
||||
_checkValue.value = Result.Error(exception)
|
||||
|
||||
// Log the error for debugging
|
||||
Log.e("RegisterViewModel", "Error:", exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5
app/src/main/res/drawable/baseline_info_24.xml
Normal file
5
app/src/main/res/drawable/baseline_info_24.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#211E1E" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/>
|
||||
|
||||
</vector>
|
5
app/src/main/res/drawable/baseline_logout_24.xml
Normal file
5
app/src/main/res/drawable/baseline_logout_24.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="#211E1E" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M17,7l-1.41,1.41L18.17,11H8v2h10.17l-2.58,2.58L17,17l5,-5zM4,5h8V3H4c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h8v-2H4V5z"/>
|
||||
|
||||
</vector>
|
8
app/src/main/res/drawable/bg_bottom_sheet.xml
Normal file
8
app/src/main/res/drawable/bg_bottom_sheet.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/white" />
|
||||
<corners
|
||||
android:topLeftRadius="16dp"
|
||||
android:topRightRadius="16dp" />
|
||||
</shape>
|
8
app/src/main/res/drawable/bg_spinner_reason.xml
Normal file
8
app/src/main/res/drawable/bg_spinner_reason.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/white" />
|
||||
<corners android:radius="8dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/light_gray" />
|
||||
</shape>
|
579
app/src/main/res/layout/activity_register_store.xml
Normal file
579
app/src/main/res/layout/activity_register_store.xml
Normal file
@ -0,0 +1,579 @@
|
||||
<?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.auth.RegisterStoreActivity">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="#334583"
|
||||
android:padding="16dp"
|
||||
android:text="Buka Toko"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="#334583"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:text="Mohon untuk melengkapi formulir pendaftaran ini agar dapat mengakses fitur penjual pada aplikasi."
|
||||
android:textColor="@android:color/white" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Main content in ScrollView -->
|
||||
<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:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="* Wajib diisi"
|
||||
android:textColor="#777777" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="1. Nama Toko *"
|
||||
android:textColor="@android:color/black" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_store_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@android:drawable/editbox_background"
|
||||
android:hint="Isi jawaban Anda di sini"
|
||||
android:inputType="text"
|
||||
android:padding="12dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="2. Deskripsi Toko"
|
||||
android:textColor="@android:color/black" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_store_description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@android:drawable/editbox_background"
|
||||
android:gravity="top"
|
||||
android:hint="Isi jawaban Anda di sini"
|
||||
android:inputType="textMultiLine"
|
||||
android:padding="12dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="3. Jenis UMKM *"
|
||||
android:textColor="@android:color/black" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner_store_type"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@android:drawable/btn_dropdown"
|
||||
android:hint="Pilih jawaban Anda di sini"
|
||||
android:padding="12dp" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/storeTypeProgressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="4dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="4. Provinsi *"
|
||||
android:textColor="@android:color/black" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner_province"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@android:drawable/btn_dropdown"
|
||||
android:hint="Pilih jawaban Anda di sini"
|
||||
android:padding="12dp" />
|
||||
<ProgressBar
|
||||
android:id="@+id/provinceProgressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="4dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="5. Kabupaten/Kota *"
|
||||
android:textColor="@android:color/black" />
|
||||
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner_city"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@android:drawable/btn_dropdown"
|
||||
android:hint="Pilih jawaban Anda di sini"
|
||||
android:padding="12dp" />
|
||||
<ProgressBar
|
||||
android:id="@+id/cityProgressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="4dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="6. Kecamatan *"
|
||||
android:textColor="@android:color/black" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_subdistrict"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@android:drawable/editbox_background"
|
||||
android:hint="Isi jawaban Anda di sini"
|
||||
android:inputType="text"
|
||||
android:padding="12dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="7. Jalan *"
|
||||
android:textColor="@android:color/black" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_street"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@android:drawable/editbox_background"
|
||||
android:hint="Isi jawaban Anda di sini"
|
||||
android:inputType="text"
|
||||
android:padding="12dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="8. Kode Pos *"
|
||||
android:textColor="@android:color/black" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_postal_code"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@android:drawable/editbox_background"
|
||||
android:hint="Isi jawaban Anda di sini"
|
||||
android:inputType="number"
|
||||
android:padding="12dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="9. Detail Alamat"
|
||||
android:textColor="@android:color/black" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_address_detail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@android:drawable/editbox_background"
|
||||
android:gravity="top"
|
||||
android:hint="Isi jawaban Anda di sini"
|
||||
android:inputType="textMultiLine"
|
||||
android:padding="12dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="10. Bank *"
|
||||
android:textColor="@android:color/black" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_bank_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@android:drawable/editbox_background"
|
||||
android:hint="Isi jawaban Anda di sini"
|
||||
android:inputType="text"
|
||||
android:padding="12dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="11. Nomor Rekening *"
|
||||
android:textColor="@android:color/black" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_bank_number"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@android:drawable/editbox_background"
|
||||
android:hint="Isi jawaban Anda di sini"
|
||||
android:inputType="number"
|
||||
android:padding="12dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="12. Pilih Kurir *"
|
||||
android:textColor="@android:color/black" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkbox_jne"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="JNE" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkbox_jnt"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:text="J&T" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkbox_pos"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:text="POS" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="13. Foto Profil Toko"
|
||||
android:textColor="@android:color/black" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/container_store_img"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@android:drawable/editbox_background">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/img_store"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:visibility="gone" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_upload_store_img"
|
||||
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>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="14. Dokumen KTP *"
|
||||
android:textColor="@android:color/black" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/container_ktp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@android:drawable/editbox_background">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/img_ktp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:visibility="gone" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_upload_ktp"
|
||||
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>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="15. Dokumen NIB *"
|
||||
android:textColor="@android:color/black" />
|
||||
|
||||
<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>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="16. Dokumen NPWP *"
|
||||
android:textColor="@android:color/black" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/container_npwp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@android:drawable/editbox_background">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/img_npwp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:visibility="gone" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_upload_npwp"
|
||||
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>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="16. Dokumen SPPIRT"
|
||||
android:textColor="@android:color/black" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/container_sppirt"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@android:drawable/editbox_background">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/img_sppirt"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:visibility="gone" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_upload_sppirt"
|
||||
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>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="17. Dokumen Sertifikat Halal"
|
||||
android:textColor="@android:color/black" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/container_halal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@android:drawable/editbox_background">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/img_halal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:visibility="gone" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_upload_halal"
|
||||
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>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="18. Pilih Titik Lokasi Usaha *"
|
||||
android:textColor="@android:color/black" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/map_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@android:color/darker_gray">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/placeholder_image" />
|
||||
</FrameLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_register"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:background="#3F9CE8"
|
||||
android:text="Daftar"
|
||||
android:textColor="@android:color/white" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
</LinearLayout>
|
@ -49,7 +49,7 @@
|
||||
android:text="@string/fragment_home_categories"
|
||||
android:textColor="@color/blue_500"
|
||||
android:fontFamily="@font/dmsans_bold"
|
||||
android:textSize="22sp"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/banners" />
|
||||
|
||||
@ -89,7 +89,7 @@
|
||||
android:text="@string/sold_product_text"
|
||||
android:textColor="@color/blue_500"
|
||||
android:fontFamily="@font/dmsans_bold"
|
||||
android:textSize="22sp"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/categories" />
|
||||
<com.google.android.material.button.MaterialButton
|
||||
@ -101,7 +101,7 @@
|
||||
android:text="@string/show_all"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@color/blue_600"
|
||||
android:textSize="16sp"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBaseline_toBaselineOf="@id/new_products_text"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:src="@drawable/outline_account_circle_24"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
@ -118,6 +119,7 @@
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:paddingVertical="8dp"
|
||||
android:textSize="16sp"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:text="Pesanan Saya"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@ -129,7 +131,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:textSize="12sp"
|
||||
android:textSize="14sp"
|
||||
android:padding="0dp"
|
||||
android:fontFamily="@font/dmsans_light"
|
||||
android:text="Lihat Riwayat Pesanan"
|
||||
@ -164,6 +166,8 @@
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp"
|
||||
android:fontFamily="@font/dmsans_regular"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/waiting_payment" />
|
||||
</LinearLayout>
|
||||
@ -185,6 +189,8 @@
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/dmsans_regular"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/packages" />
|
||||
</LinearLayout>
|
||||
@ -206,6 +212,8 @@
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/dmsans_regular"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/delivery" />
|
||||
</LinearLayout>
|
||||
@ -223,103 +231,176 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:padding="16dp"
|
||||
android:textSize="16sp"
|
||||
android:text="Pengaturan Akun"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
app:layout_constraintTop_toBottomOf="@id/cardPesanan" />
|
||||
|
||||
<!-- Address -->
|
||||
<ImageView
|
||||
android:id="@+id/ivAddress"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:src="@drawable/ic_address"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tvAddress"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tvAddress" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvAddress"
|
||||
android:layout_width="0dp"
|
||||
<LinearLayout
|
||||
android:id="@+id/container_settings"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
android:text="Alamat"
|
||||
app:layout_constraintEnd_toStartOf="@id/ivAddressArrow"
|
||||
app:layout_constraintStart_toEndOf="@id/ivAddress"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvPengaturanAkun" />
|
||||
app:layout_constraintTop_toBottomOf="@id/tvPengaturanAkun"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<!-- Address Card -->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/card_address"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:cardElevation="2dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivAddress"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/ic_address"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvAddress"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:text="Alamat"
|
||||
android:textSize="14sp"
|
||||
android:fontFamily="@font/dmsans_regular"
|
||||
app:layout_constraintStart_toEndOf="@id/ivAddress"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/ivAddressArrow" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivAddressArrow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_arrow_right"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<!-- About Card -->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/card_about"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:cardElevation="2dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivAbout"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/baseline_info_24"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvAbout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:text="Tentang"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintStart_toEndOf="@id/ivAbout"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/ivAboutArrow" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivAboutArrow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_arrow_right"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<!-- Logout Card -->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/card_logout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:cardElevation="2dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivLogout"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/baseline_logout_24"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvLogout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:text="Keluar"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintStart_toEndOf="@id/ivLogout"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/ivLogoutArrow" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivLogoutArrow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_arrow_right"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivAddressArrow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:src="@drawable/ic_arrow_right"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tvAddress"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tvAddress" />
|
||||
|
||||
<!-- About -->
|
||||
<ImageView
|
||||
android:id="@+id/ivAbout"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tvAbout"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tvAbout" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvAbout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:padding="16dp"
|
||||
android:text="Tentang"
|
||||
app:layout_constraintEnd_toStartOf="@id/ivAboutArrow"
|
||||
app:layout_constraintStart_toEndOf="@id/ivAbout"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvAddress" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivAboutArrow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:src="@drawable/ic_arrow_right"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tvAbout"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tvAbout" />
|
||||
|
||||
<!-- Logout -->
|
||||
<ImageView
|
||||
android:id="@+id/ivLogout"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tvLogout"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tvLogout" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvLogout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:padding="16dp"
|
||||
android:text="Keluar"
|
||||
app:layout_constraintEnd_toStartOf="@id/ivLogoutArrow"
|
||||
app:layout_constraintStart_toEndOf="@id/ivLogout"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvAbout" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivLogoutArrow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:src="@drawable/ic_arrow_right"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tvLogout"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tvLogout" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
8
app/src/main/res/layout/item_cancel_order.xml
Normal file
8
app/src/main/res/layout/item_cancel_order.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/tv_reason"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="14sp" />
|
@ -7,11 +7,12 @@
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/imageLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="0dp"
|
||||
app:cardCornerRadius="14dp"
|
||||
app:layout_constraintDimensionRatio="272:218"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:cardElevation="0dp"
|
||||
app:strokeColor="@color/gray_1"
|
||||
app:strokeWidth="1dp">
|
||||
@ -32,7 +33,7 @@
|
||||
android:text="Banana"
|
||||
android:textColor="@color/black"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:textSize="18sp"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintTop_toBottomOf="@id/imageLayout" />
|
||||
|
||||
<TextView
|
||||
@ -42,7 +43,7 @@
|
||||
android:text="@string/item_price_txt"
|
||||
android:textColor="@color/black"
|
||||
android:textStyle="bold"
|
||||
android:textSize="18sp"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintTop_toBottomOf="@id/item_name" />
|
||||
|
||||
<TextView
|
||||
@ -58,7 +59,7 @@
|
||||
android:text="@string/rating"
|
||||
android:textColor="@color/black"
|
||||
android:fontFamily="@font/dmsans_regular"
|
||||
android:textSize="14sp"
|
||||
android:textSize="12sp"
|
||||
android:textAlignment="center"
|
||||
android:gravity="center"
|
||||
app:drawableStartCompat="@drawable/baseline_star_24"
|
||||
|
@ -1,16 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="start"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginVertical="8dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:orientation="horizontal">
|
||||
<RadioButton
|
||||
@ -27,7 +29,7 @@
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:id="@+id/courier_name_cost"
|
||||
android:fontFamily="@font/dmsans_bold"
|
||||
android:fontFamily="@font/dmsans_semibold"
|
||||
android:textSize="20sp"
|
||||
android:padding="4dp"
|
||||
android:layout_width="wrap_content"
|
||||
@ -40,8 +42,8 @@
|
||||
android:textSize="16sp"
|
||||
android:paddingHorizontal="8dp"
|
||||
android:text="Estimasi 3-4 hari"/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/cost_price"
|
||||
@ -53,12 +55,16 @@
|
||||
android:layout_height="match_parent"
|
||||
android:text="Rp15.0000"/>
|
||||
|
||||
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
<View
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:background="#E0E0E0"
|
||||
app:layout_constraintTop_toBottomOf="@id/linear_toolbar" />
|
||||
app:layout_constraintTop_toBottomOf="@id/content" />
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
73
app/src/main/res/layout/layout_cancel_order_bottom.xml
Normal file
73
app/src/main/res/layout/layout_cancel_order_bottom.xml
Normal file
@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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:background="@drawable/bg_bottom_sheet"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/dmsans_semibold"
|
||||
android:text="Batalkan Pesanan"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_reason_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="Alasan batalkan pesanan:"
|
||||
android:fontFamily="@font/dmsans_regular"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/tv_title" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner_reason"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/bg_spinner_reason"
|
||||
android:padding="12dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/tv_reason_label" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_cancel"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:backgroundTint="@color/black_200"
|
||||
android:text="Kembali"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:textColor="@color/white"
|
||||
app:layout_constraintEnd_toStartOf="@+id/btn_confirm"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/spinner_reason" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_confirm"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:backgroundTint="@color/blue_500"
|
||||
android:text="Batal"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:textColor="@color/white"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/btn_cancel"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/btn_cancel"
|
||||
app:layout_constraintTop_toTopOf="@+id/btn_cancel" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -30,6 +30,7 @@
|
||||
android:backgroundTint="@color/white"
|
||||
android:src="@drawable/outline_notifications_24"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/search"
|
||||
app:layout_constraintEnd_toStartOf="@id/btn_cart"
|
||||
app:layout_constraintTop_toTopOf="@id/search"/>
|
||||
|
@ -48,7 +48,7 @@
|
||||
|
||||
<style name="BottomNavigationTextStyle" parent="TextAppearance.MaterialComponents.Caption">
|
||||
<item name="android:textSize">14sp</item>
|
||||
<item name="fontFamily">@font/dmsans_semibold</item>
|
||||
<item name="fontFamily">@font/dmsans_medium</item>
|
||||
<item name="android:paddingTop">8dp</item>
|
||||
<item name="android:layout_marginTop">4dp</item>
|
||||
</style>
|
||||
|
Reference in New Issue
Block a user