mirror of
https://github.com/shaulascr/ecommerce_serang.git
synced 2025-12-16 16:01:02 +00:00
Compare commits
18 Commits
screen-fea
...
fe5ecf28e5
| Author | SHA1 | Date | |
|---|---|---|---|
| fe5ecf28e5 | |||
| 3d733b7e0f | |||
| b3d2527ebc | |||
| 16a0a33f11 | |||
| 45fddf6116 | |||
| 1d9399fd4d | |||
| 0887a7e898 | |||
| 29fb55e3c0 | |||
| 2f16542e5e | |||
| 2f28a23114 | |||
| 94c081e839 | |||
| d32bdf65fe | |||
| 9162b2cc60 | |||
| 8eac90311e | |||
| 9cd0675d82 | |||
| f88a5a46ad | |||
| 421c20cc4b | |||
| 792e247eaa |
4
.idea/deploymentTargetSelector.xml
generated
4
.idea/deploymentTargetSelector.xml
generated
@ -4,10 +4,10 @@
|
|||||||
<selectionStates>
|
<selectionStates>
|
||||||
<SelectionState runConfigName="app">
|
<SelectionState runConfigName="app">
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
<DropdownSelection timestamp="2025-05-08T14:50:55.425322500Z">
|
<DropdownSelection timestamp="2025-08-17T17:32:55.497700100Z">
|
||||||
<Target type="DEFAULT_BOOT">
|
<Target type="DEFAULT_BOOT">
|
||||||
<handle>
|
<handle>
|
||||||
<DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\Gracia Hotmauli\.android\avd\Pixel_8_2_2.avd" />
|
<DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\Gracia Hotmauli\.android\avd\Pixel_9_2.avd" />
|
||||||
</handle>
|
</handle>
|
||||||
</Target>
|
</Target>
|
||||||
</DropdownSelection>
|
</DropdownSelection>
|
||||||
|
|||||||
@ -124,4 +124,7 @@ dependencies {
|
|||||||
implementation(platform("com.google.firebase:firebase-bom:33.13.0"))
|
implementation(platform("com.google.firebase:firebase-bom:33.13.0"))
|
||||||
implementation("com.google.firebase:firebase-analytics")
|
implementation("com.google.firebase:firebase-analytics")
|
||||||
implementation("com.google.firebase:firebase-messaging-ktx")
|
implementation("com.google.firebase:firebase-messaging-ktx")
|
||||||
|
|
||||||
|
//Splash screen
|
||||||
|
implementation("androidx.core:core-splashscreen:1.0.0")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,6 +29,9 @@
|
|||||||
android:theme="@style/Theme.Ecommerce_serang"
|
android:theme="@style/Theme.Ecommerce_serang"
|
||||||
android:usesCleartextTraffic="true"
|
android:usesCleartextTraffic="true"
|
||||||
tools:targetApi="31">
|
tools:targetApi="31">
|
||||||
|
<activity
|
||||||
|
android:name=".ui.profile.ChangePasswordActivity"
|
||||||
|
android:exported="false" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.auth.ResetPassActivity"
|
android:name=".ui.auth.ResetPassActivity"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
@ -170,7 +173,8 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".ui.auth.RegisterActivity"
|
android:name=".ui.auth.RegisterActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize"
|
||||||
|
android:theme="@style/Theme.App.SplashScreen">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,6 @@
|
|||||||
|
package com.alya.ecommerce_serang.data.api.dto
|
||||||
|
|
||||||
|
data class ChangePasswordRequest(
|
||||||
|
val currentPassword: String,
|
||||||
|
val newPassword: String
|
||||||
|
)
|
||||||
@ -10,9 +10,6 @@ data class Store(
|
|||||||
@field:SerializedName("store_status")
|
@field:SerializedName("store_status")
|
||||||
val storeStatus: String,
|
val storeStatus: String,
|
||||||
|
|
||||||
@field:SerializedName("sppirt")
|
|
||||||
val sppirt: String,
|
|
||||||
|
|
||||||
@field:SerializedName("user_name")
|
@field:SerializedName("user_name")
|
||||||
val userName: String,
|
val userName: String,
|
||||||
|
|
||||||
@ -37,9 +34,6 @@ data class Store(
|
|||||||
@field:SerializedName("user_phone")
|
@field:SerializedName("user_phone")
|
||||||
val userPhone: String,
|
val userPhone: String,
|
||||||
|
|
||||||
@field:SerializedName("halal")
|
|
||||||
val halal: String,
|
|
||||||
|
|
||||||
@field:SerializedName("id")
|
@field:SerializedName("id")
|
||||||
val id: Int,
|
val id: Int,
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,9 @@
|
|||||||
|
package com.alya.ecommerce_serang.data.api.response.auth
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
data class ChangePassResponse(
|
||||||
|
|
||||||
|
@field:SerializedName("message")
|
||||||
|
val message: String? = null
|
||||||
|
)
|
||||||
@ -1,18 +1,13 @@
|
|||||||
package com.alya.ecommerce_serang.data.api.response.store
|
package com.alya.ecommerce_serang.data.api.response.store
|
||||||
|
|
||||||
|
import com.alya.ecommerce_serang.data.api.dto.Store
|
||||||
|
import com.alya.ecommerce_serang.data.api.response.store.profile.Payment
|
||||||
|
import com.alya.ecommerce_serang.data.api.response.store.profile.Shipping
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
data class StoreResponse(
|
data class StoreResponse(
|
||||||
val message: String,
|
val message: String,
|
||||||
val store: Store
|
val store: Store,
|
||||||
)
|
val shipping: List<Shipping> = emptyList(),
|
||||||
|
val payment: List<Payment> = emptyList()
|
||||||
data class Store(
|
|
||||||
@SerializedName("store_id") val storeId: Int,
|
|
||||||
@SerializedName("store_status") val storeStatus: String,
|
|
||||||
@SerializedName("store_name") val storeName: String,
|
|
||||||
@SerializedName("user_name") val userName: String,
|
|
||||||
val email: String,
|
|
||||||
@SerializedName("user_phone") val userPhone: String,
|
|
||||||
val balance: String
|
|
||||||
)
|
)
|
||||||
@ -5,6 +5,7 @@ import com.alya.ecommerce_serang.data.api.dto.AddEvidenceRequest
|
|||||||
import com.alya.ecommerce_serang.data.api.dto.AddPaymentInfoResponse
|
import com.alya.ecommerce_serang.data.api.dto.AddPaymentInfoResponse
|
||||||
import com.alya.ecommerce_serang.data.api.dto.CancelOrderReq
|
import com.alya.ecommerce_serang.data.api.dto.CancelOrderReq
|
||||||
import com.alya.ecommerce_serang.data.api.dto.CartItem
|
import com.alya.ecommerce_serang.data.api.dto.CartItem
|
||||||
|
import com.alya.ecommerce_serang.data.api.dto.ChangePasswordRequest
|
||||||
import com.alya.ecommerce_serang.data.api.dto.CityResponse
|
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.CompletedOrderRequest
|
||||||
import com.alya.ecommerce_serang.data.api.dto.ConfirmPaymentRequest
|
import com.alya.ecommerce_serang.data.api.dto.ConfirmPaymentRequest
|
||||||
@ -27,6 +28,7 @@ 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.UpdateCart
|
||||||
import com.alya.ecommerce_serang.data.api.dto.UpdateChatRequest
|
import com.alya.ecommerce_serang.data.api.dto.UpdateChatRequest
|
||||||
import com.alya.ecommerce_serang.data.api.dto.VerifRegisReq
|
import com.alya.ecommerce_serang.data.api.dto.VerifRegisReq
|
||||||
|
import com.alya.ecommerce_serang.data.api.response.auth.ChangePassResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.auth.CheckStoreResponse
|
import com.alya.ecommerce_serang.data.api.response.auth.CheckStoreResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.auth.FcmTokenResponse
|
import com.alya.ecommerce_serang.data.api.response.auth.FcmTokenResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.auth.HasStoreResponse
|
import com.alya.ecommerce_serang.data.api.response.auth.HasStoreResponse
|
||||||
@ -530,6 +532,11 @@ interface ApiService {
|
|||||||
@Body request: ResetPassReq
|
@Body request: ResetPassReq
|
||||||
): Response<ResetPassResponse>
|
): Response<ResetPassResponse>
|
||||||
|
|
||||||
|
@POST("changepass")
|
||||||
|
suspend fun changePassword(
|
||||||
|
@Body request: ChangePasswordRequest
|
||||||
|
): Response<ChangePassResponse>
|
||||||
|
|
||||||
@GET("profile/address/detail/{id}")
|
@GET("profile/address/detail/{id}")
|
||||||
suspend fun getDetailAddress(
|
suspend fun getDetailAddress(
|
||||||
@Path("id") addressId: Int
|
@Path("id") addressId: Int
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import android.util.Log
|
|||||||
import com.alya.ecommerce_serang.data.api.dto.ProductsItem
|
import com.alya.ecommerce_serang.data.api.dto.ProductsItem
|
||||||
import com.alya.ecommerce_serang.data.api.dto.Store
|
import com.alya.ecommerce_serang.data.api.dto.Store
|
||||||
import com.alya.ecommerce_serang.data.api.response.auth.ListStoreTypeResponse
|
import com.alya.ecommerce_serang.data.api.response.auth.ListStoreTypeResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.customer.product.StoreResponse
|
import com.alya.ecommerce_serang.data.api.response.store.StoreResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.store.profile.StoreDataResponse
|
import com.alya.ecommerce_serang.data.api.response.store.profile.StoreDataResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.store.sells.OrderListResponse
|
import com.alya.ecommerce_serang.data.api.response.store.sells.OrderListResponse
|
||||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiService
|
import com.alya.ecommerce_serang.data.api.retrofit.ApiService
|
||||||
@ -15,13 +15,13 @@ import retrofit2.Response
|
|||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class MyStoreRepository(private val apiService: ApiService) {
|
class MyStoreRepository(private val apiService: ApiService) {
|
||||||
suspend fun fetchMyStoreProfile(): Result<Store?> {
|
suspend fun fetchMyStoreProfile(): Result<StoreResponse?> {
|
||||||
return try {
|
return try {
|
||||||
val response = apiService.getStore()
|
val response = apiService.getMyStoreData()
|
||||||
|
|
||||||
if (response.isSuccessful) {
|
if (response.isSuccessful) {
|
||||||
val storeResponse: StoreResponse? = response.body()
|
val storeResponse = response.body()
|
||||||
Result.Success(storeResponse?.store)
|
Result.Success(storeResponse)
|
||||||
} else {
|
} else {
|
||||||
val errorMessage = response.errorBody()?.string() ?: "Unknown API error"
|
val errorMessage = response.errorBody()?.string() ?: "Unknown API error"
|
||||||
Log.e("MyStoreRepository", "Error: $errorMessage")
|
Log.e("MyStoreRepository", "Error: $errorMessage")
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package com.alya.ecommerce_serang.data.repository
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import com.alya.ecommerce_serang.data.api.dto.ChangePasswordRequest
|
||||||
import com.alya.ecommerce_serang.data.api.dto.FcmReq
|
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.LoginRequest
|
||||||
import com.alya.ecommerce_serang.data.api.dto.OtpRequest
|
import com.alya.ecommerce_serang.data.api.dto.OtpRequest
|
||||||
@ -10,6 +11,7 @@ import com.alya.ecommerce_serang.data.api.dto.RegisterRequest
|
|||||||
import com.alya.ecommerce_serang.data.api.dto.ResetPassReq
|
import com.alya.ecommerce_serang.data.api.dto.ResetPassReq
|
||||||
import com.alya.ecommerce_serang.data.api.dto.UserProfile
|
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.dto.VerifRegisReq
|
||||||
|
import com.alya.ecommerce_serang.data.api.response.auth.ChangePassResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.auth.FcmTokenResponse
|
import com.alya.ecommerce_serang.data.api.response.auth.FcmTokenResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.auth.HasStoreResponse
|
import com.alya.ecommerce_serang.data.api.response.auth.HasStoreResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.auth.ListStoreTypeResponse
|
import com.alya.ecommerce_serang.data.api.response.auth.ListStoreTypeResponse
|
||||||
@ -516,6 +518,29 @@ class UserRepository(private val apiService: ApiService) {
|
|||||||
Result.Error(e)
|
Result.Error(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun changePassword(currentPassword: String, newPassword: String): Result<ChangePassResponse> {
|
||||||
|
return try {
|
||||||
|
val request = ChangePasswordRequest(currentPassword, newPassword)
|
||||||
|
val response = apiService.changePassword(request) // Make the API call
|
||||||
|
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
val changePassResponse = response.body()
|
||||||
|
if (changePassResponse != null) {
|
||||||
|
Result.Success(changePassResponse) // Return success with the response message
|
||||||
|
} else {
|
||||||
|
Result.Error(Exception("Empty response from server"))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val errorBody = response.errorBody()?.string() ?: "Unknown error"
|
||||||
|
Log.e(TAG, "Error changing password: $errorBody")
|
||||||
|
Result.Error(Exception(errorBody))
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Result.Error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object{
|
companion object{
|
||||||
private const val TAG = "UserRepository"
|
private const val TAG = "UserRepository"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,27 +43,10 @@ class LoginActivity : AppCompatActivity() {
|
|||||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
|
|
||||||
// ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view, windowInsets ->
|
|
||||||
// val systemBars = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
|
||||||
// view.setPadding(
|
|
||||||
// systemBars.left,
|
|
||||||
// systemBars.top,
|
|
||||||
// systemBars.right,
|
|
||||||
// systemBars.bottom
|
|
||||||
// )
|
|
||||||
// windowInsets
|
|
||||||
// }
|
|
||||||
|
|
||||||
// onBackPressedDispatcher.addCallback(this) {
|
|
||||||
// // Handle the back button event
|
|
||||||
// }
|
|
||||||
|
|
||||||
setupListeners()
|
setupListeners()
|
||||||
observeLoginState()
|
observeLoginState()
|
||||||
|
|
||||||
FirebaseApp.initializeApp(this)
|
FirebaseApp.initializeApp(this)
|
||||||
|
|
||||||
// Request FCM token at app startup
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupListeners() {
|
private fun setupListeners() {
|
||||||
@ -72,7 +55,7 @@ class LoginActivity : AppCompatActivity() {
|
|||||||
val password = binding.etLoginPassword.text.toString()
|
val password = binding.etLoginPassword.text.toString()
|
||||||
|
|
||||||
if (email.isEmpty() || password.isEmpty()) {
|
if (email.isEmpty() || password.isEmpty()) {
|
||||||
Toast.makeText(this, "Please fill in all fields", Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, "Mohon masukkan email atau password dengan benar", Toast.LENGTH_SHORT).show()
|
||||||
} else {
|
} else {
|
||||||
loginViewModel.login(email, password)
|
loginViewModel.login(email, password)
|
||||||
}
|
}
|
||||||
@ -100,14 +83,14 @@ class LoginActivity : AppCompatActivity() {
|
|||||||
retrieveFCMToken()
|
retrieveFCMToken()
|
||||||
// sessionManager.saveUserId(response.userId)
|
// sessionManager.saveUserId(response.userId)
|
||||||
|
|
||||||
Toast.makeText(this, "Login Successful", Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, "Berhasil masuk", Toast.LENGTH_SHORT).show()
|
||||||
|
|
||||||
startActivity(Intent(this, MainActivity::class.java))
|
startActivity(Intent(this, MainActivity::class.java))
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
is com.alya.ecommerce_serang.data.repository.Result.Error -> {
|
is com.alya.ecommerce_serang.data.repository.Result.Error -> {
|
||||||
Log.e("LoginActivity", "Login Failed: ${result.exception.message}")
|
Log.e("LoginActivity", "Login Failed: ${result.exception.message}")
|
||||||
Toast.makeText(this, "Login Failed: ${result.exception.message}", Toast.LENGTH_LONG).show()
|
Toast.makeText(this, "Gagal masuk", Toast.LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
is Result.Loading -> {
|
is Result.Loading -> {
|
||||||
// Show loading state
|
// Show loading state
|
||||||
|
|||||||
@ -29,7 +29,7 @@ class OtpBottomSheetDialog(
|
|||||||
onRegister(updatedUserData) // Send full data to ViewModel
|
onRegister(updatedUserData) // Send full data to ViewModel
|
||||||
dismiss() // Close dialog
|
dismiss() // Close dialog
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(requireContext(), "Please enter OTP", Toast.LENGTH_SHORT).show()
|
Toast.makeText(requireContext(), "Silahkan masukkan kode OTP", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return view
|
return view
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import android.util.Log
|
|||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
@ -39,6 +40,8 @@ class RegisterActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
Thread.sleep(3000)
|
||||||
|
installSplashScreen()
|
||||||
binding = ActivityRegisterBinding.inflate(layoutInflater)
|
binding = ActivityRegisterBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
sessionManager = SessionManager(this)
|
sessionManager = SessionManager(this)
|
||||||
@ -86,20 +89,72 @@ class RegisterActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// navigate step register in fragment
|
// In RegisterActivity, add debug to navigateToStep:
|
||||||
|
|
||||||
fun navigateToStep(step: Int, userData: RegisterRequest?) {
|
fun navigateToStep(step: Int, userData: RegisterRequest?) {
|
||||||
val fragment = when (step) {
|
Log.d("RegisterActivity", "=== NAVIGATE TO STEP START ===")
|
||||||
1 -> RegisterStep1Fragment.newInstance()
|
Log.d("RegisterActivity", "Target step: $step")
|
||||||
2 -> RegisterStep2Fragment.newInstance(userData)
|
Log.d("RegisterActivity", "Current fragment count: ${supportFragmentManager.fragments.size}")
|
||||||
3 -> RegisterStep3Fragment.newInstance()
|
Log.d("RegisterActivity", "UserData: ${userData?.email}")
|
||||||
else -> null
|
|
||||||
|
Log.d("RegisterActivity", "Navigation called from:")
|
||||||
|
Thread.currentThread().stackTrace.take(10).forEach { element ->
|
||||||
|
Log.d("RegisterActivity", " at ${element.className}.${element.methodName}(${element.fileName}:${element.lineNumber})")
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment?.let {
|
|
||||||
supportFragmentManager.beginTransaction()
|
try {
|
||||||
.replace(R.id.fragment_container, it)
|
val fragment = when (step) {
|
||||||
.addToBackStack(null)
|
1 -> {
|
||||||
.commit()
|
Log.d("RegisterActivity", "Creating RegisterStep1Fragment")
|
||||||
|
RegisterStep1Fragment.newInstance()
|
||||||
|
}
|
||||||
|
2 -> {
|
||||||
|
Log.d("RegisterActivity", "Creating RegisterStep2Fragment")
|
||||||
|
RegisterStep2Fragment.newInstance(userData)
|
||||||
|
}
|
||||||
|
3 -> {
|
||||||
|
Log.d("RegisterActivity", "Creating RegisterStep3Fragment")
|
||||||
|
RegisterStep3Fragment.newInstance()
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Log.e("RegisterActivity", "Invalid step: $step")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d("RegisterActivity", "Fragment created, starting transaction")
|
||||||
|
|
||||||
|
val transaction = supportFragmentManager.beginTransaction()
|
||||||
|
transaction.replace(R.id.fragment_container, fragment)
|
||||||
|
|
||||||
|
Log.d("RegisterActivity", "About to commit transaction")
|
||||||
|
transaction.commit()
|
||||||
|
|
||||||
|
Log.d("RegisterActivity", "Transaction committed")
|
||||||
|
|
||||||
|
// Update ViewModel step
|
||||||
|
registerViewModel.setStep(step)
|
||||||
|
Log.d("RegisterActivity", "ViewModel step updated to: $step")
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("RegisterActivity", "Exception in navigateToStep: ${e.message}", e)
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d("RegisterActivity", "=== NAVIGATE TO STEP END ===")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle Android back button - close activity or go to step 1
|
||||||
|
override fun onBackPressed() {
|
||||||
|
val currentStep = registerViewModel.currentStep.value ?: 1
|
||||||
|
|
||||||
|
if (currentStep == 1) {
|
||||||
|
// On step 1, exit the activity
|
||||||
|
super.onBackPressed()
|
||||||
|
} else {
|
||||||
|
// On other steps, go back to step 1
|
||||||
|
navigateToStep(1, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,7 +111,7 @@ class ResetPassActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleError(errorMessage: String) {
|
private fun handleError(errorMessage: String) {
|
||||||
Toast.makeText(this, "Error: $errorMessage", Toast.LENGTH_LONG).show()
|
Log.e(TAG, "Error: $errorMessage")
|
||||||
|
|
||||||
// Optionally show error dialog
|
// Optionally show error dialog
|
||||||
AlertDialog.Builder(this)
|
AlertDialog.Builder(this)
|
||||||
|
|||||||
@ -155,19 +155,20 @@ class RegisterStep1Fragment : Fragment() {
|
|||||||
"email" -> {
|
"email" -> {
|
||||||
isEmailValid = isValid
|
isEmailValid = isValid
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
Toast.makeText(requireContext(), "Email is already registered", Toast.LENGTH_SHORT).show()
|
Toast.makeText(requireContext(), "Email sudah digunakan. Gunakan email lainnya.", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"phone" -> {
|
"phone" -> {
|
||||||
isPhoneValid = isValid
|
isPhoneValid = isValid
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
Toast.makeText(requireContext(), "Phone number is already registered", Toast.LENGTH_SHORT).show()
|
Toast.makeText(requireContext(), "Nomor handphone sudah digunakan. Gunakan nomor lainnya. ", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is com.alya.ecommerce_serang.data.repository.Result.Error -> {
|
is com.alya.ecommerce_serang.data.repository.Result.Error -> {
|
||||||
Toast.makeText(requireContext(), "Validation failed: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
Toast.makeText(requireContext(), "Gagal melakukan validasi", Toast.LENGTH_SHORT).show()
|
||||||
|
Log.e(TAG, "Validation failed: ${result.exception.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -200,7 +201,8 @@ class RegisterStep1Fragment : Fragment() {
|
|||||||
is Result.Error -> {
|
is Result.Error -> {
|
||||||
binding.progressBar.visibility = View.GONE
|
binding.progressBar.visibility = View.GONE
|
||||||
binding.btnNext.isEnabled = true
|
binding.btnNext.isEnabled = true
|
||||||
Toast.makeText(requireContext(), "OTP Request Failed: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
Log.e(TAG, "OTP Request Failed: ${result.exception.message}")
|
||||||
|
Toast.makeText(requireContext(), "Gagal mendapatkan OTP. Kirim ulang OTP", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -229,13 +231,13 @@ class RegisterStep1Fragment : Fragment() {
|
|||||||
// Check if all fields are filled
|
// Check if all fields are filled
|
||||||
if (email.isEmpty() || password.isEmpty() || confirmPassword.isEmpty() || phone.isEmpty() ||
|
if (email.isEmpty() || password.isEmpty() || confirmPassword.isEmpty() || phone.isEmpty() ||
|
||||||
username.isEmpty() || fullName.isEmpty() || birthDate.isEmpty()) {
|
username.isEmpty() || fullName.isEmpty() || birthDate.isEmpty()) {
|
||||||
Toast.makeText(requireContext(), "Please fill all required fields", Toast.LENGTH_SHORT).show()
|
Toast.makeText(requireContext(), "Silahkan lengkapi seluruh isian", Toast.LENGTH_SHORT).show()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if passwords match
|
// Check if passwords match
|
||||||
if (password != confirmPassword) {
|
if (password != confirmPassword) {
|
||||||
Toast.makeText(requireContext(), "Passwords do not match", Toast.LENGTH_SHORT).show()
|
Toast.makeText(requireContext(), "Konfirmasi kata sandi tidak sesua. Periksa kembali", Toast.LENGTH_SHORT).show()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,7 +255,7 @@ class RegisterStep1Fragment : Fragment() {
|
|||||||
if (isEmailValid && isPhoneValid) {
|
if (isEmailValid && isPhoneValid) {
|
||||||
requestOtp(email)
|
requestOtp(email)
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(requireContext(), "Please fix validation errors before proceeding", Toast.LENGTH_SHORT).show()
|
Toast.makeText(requireContext(), "Silahkan perbaiki data yang dimasukkan", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
package com.alya.ecommerce_serang.ui.auth.fragments
|
package com.alya.ecommerce_serang.ui.auth.fragments
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.CountDownTimer
|
import android.os.CountDownTimer
|
||||||
@ -13,6 +15,7 @@ import androidx.core.content.ContextCompat
|
|||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import com.alya.ecommerce_serang.R
|
import com.alya.ecommerce_serang.R
|
||||||
|
import com.alya.ecommerce_serang.data.api.dto.FcmReq
|
||||||
import com.alya.ecommerce_serang.data.api.dto.RegisterRequest
|
import com.alya.ecommerce_serang.data.api.dto.RegisterRequest
|
||||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||||
import com.alya.ecommerce_serang.data.repository.OrderRepository
|
import com.alya.ecommerce_serang.data.repository.OrderRepository
|
||||||
@ -24,11 +27,15 @@ import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
|||||||
import com.alya.ecommerce_serang.utils.SessionManager
|
import com.alya.ecommerce_serang.utils.SessionManager
|
||||||
import com.alya.ecommerce_serang.utils.viewmodel.RegisterViewModel
|
import com.alya.ecommerce_serang.utils.viewmodel.RegisterViewModel
|
||||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
|
import com.google.firebase.messaging.FirebaseMessaging
|
||||||
|
|
||||||
class RegisterStep2Fragment : Fragment() {
|
class RegisterStep2Fragment : Fragment() {
|
||||||
private var _binding: FragmentRegisterStep2Binding? = null
|
private var _binding: FragmentRegisterStep2Binding? = null
|
||||||
private val binding get() = _binding!!
|
private val binding get() = _binding!!
|
||||||
private lateinit var sessionManager: SessionManager
|
private lateinit var sessionManager: SessionManager
|
||||||
|
private var countDownTimer: CountDownTimer? = null
|
||||||
|
private var timeRemaining = 30
|
||||||
|
private var isTimerRunning = false
|
||||||
|
|
||||||
// In RegisterStep2Fragment AND RegisterStep3Fragment:
|
// In RegisterStep2Fragment AND RegisterStep3Fragment:
|
||||||
private val registerViewModel: RegisterViewModel by activityViewModels {
|
private val registerViewModel: RegisterViewModel by activityViewModels {
|
||||||
@ -39,8 +46,8 @@ class RegisterStep2Fragment : Fragment() {
|
|||||||
RegisterViewModel(userRepository, orderRepository, requireContext())
|
RegisterViewModel(userRepository, orderRepository, requireContext())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private var countDownTimer: CountDownTimer? = null
|
// private var countDownTimer: CountDownTimer? = null
|
||||||
private var timeRemaining = 30 // 30 seconds cooldown for resend
|
// private var timeRemaining = 30 // 30 seconds cooldown for resend
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
@ -112,6 +119,20 @@ class RegisterStep2Fragment : Fragment() {
|
|||||||
observeRegistrationState()
|
observeRegistrationState()
|
||||||
observeLoginState()
|
observeLoginState()
|
||||||
Log.d(TAG, "Registration and login state observers set up")
|
Log.d(TAG, "Registration and login state observers set up")
|
||||||
|
binding.btnBack.setOnClickListener {
|
||||||
|
Log.d(TAG, "Back button clicked - cleaning up timer and going to step 1")
|
||||||
|
|
||||||
|
// Stop the timer before navigating
|
||||||
|
stopTimer()
|
||||||
|
// Small delay to ensure timer is properly canceled
|
||||||
|
binding.root.postDelayed({
|
||||||
|
// (activity as? RegisterActivity)?.navigateToStep(1, null)
|
||||||
|
val intent = Intent(requireContext(), RegisterActivity::class.java)
|
||||||
|
startActivity(intent)
|
||||||
|
requireActivity().finish()
|
||||||
|
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun verifyOtp(userData: RegisterRequest?) {
|
private fun verifyOtp(userData: RegisterRequest?) {
|
||||||
@ -129,11 +150,6 @@ class RegisterStep2Fragment : Fragment() {
|
|||||||
Log.d(TAG, "Updating user data with OTP: $otp")
|
Log.d(TAG, "Updating user data with OTP: $otp")
|
||||||
registerViewModel.updateUserData(updatedUserData)
|
registerViewModel.updateUserData(updatedUserData)
|
||||||
|
|
||||||
// For demo purposes, we're just proceeding to Step 3
|
|
||||||
// In a real app, you would verify the OTP with the server first
|
|
||||||
// registerViewModel.setStep(3)
|
|
||||||
// (activity as? RegisterActivity)?.navigateToStep(3, updatedUserData)
|
|
||||||
|
|
||||||
registerViewModel.registerUser(updatedUserData)
|
registerViewModel.registerUser(updatedUserData)
|
||||||
} ?: Log.e(TAG, "userData is null, cannot proceed with verification")
|
} ?: Log.e(TAG, "userData is null, cannot proceed with verification")
|
||||||
}
|
}
|
||||||
@ -170,37 +186,9 @@ class RegisterStep2Fragment : Fragment() {
|
|||||||
} ?: Log.e(TAG, "Cannot resend OTP: email is null")
|
} ?: Log.e(TAG, "Cannot resend OTP: email is null")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startResendCooldown() {
|
|
||||||
Log.d(TAG, "startResendCooldown called")
|
|
||||||
timeRemaining = 30
|
|
||||||
binding.tvResendOtp.isEnabled = false
|
|
||||||
binding.tvResendOtp.setTextColor(ContextCompat.getColor(requireContext(), R.color.soft_gray))
|
|
||||||
|
|
||||||
countDownTimer?.cancel()
|
|
||||||
countDownTimer = object : CountDownTimer(30000, 1000) {
|
|
||||||
override fun onTick(millisUntilFinished: Long) {
|
|
||||||
timeRemaining = (millisUntilFinished / 1000).toInt()
|
|
||||||
binding.tvTimer.text = "Kirim ulang OTP dalam waktu 00:${String.format("%02d", timeRemaining)}"
|
|
||||||
if (timeRemaining % 5 == 0) {
|
|
||||||
Log.d(TAG, "Cooldown remaining: $timeRemaining seconds")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFinish() {
|
|
||||||
Log.d(TAG, "Cooldown finished, enabling resend button")
|
|
||||||
binding.tvTimer.text = "Dapat mengirim ulang kode OTP"
|
|
||||||
binding.tvResendOtp.isEnabled = true
|
|
||||||
binding.tvResendOtp.setTextColor(ContextCompat.getColor(requireContext(), R.color.blue1))
|
|
||||||
timeRemaining = 0
|
|
||||||
}
|
|
||||||
}.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun observeRegistrationState() {
|
private fun observeRegistrationState() {
|
||||||
registerViewModel.message.observe(viewLifecycleOwner) { message ->
|
registerViewModel.message.observe(viewLifecycleOwner) { message ->
|
||||||
Log.d(TAG, "Message from server: $message")
|
Log.d(TAG, "Message from server: $message")
|
||||||
// You can use the message here if needed, e.g., for showing in a specific UI element
|
|
||||||
// or for storing for later use
|
|
||||||
}
|
}
|
||||||
registerViewModel.registerState.observe(viewLifecycleOwner) { result ->
|
registerViewModel.registerState.observe(viewLifecycleOwner) { result ->
|
||||||
when (result) {
|
when (result) {
|
||||||
@ -250,6 +238,8 @@ class RegisterStep2Fragment : Fragment() {
|
|||||||
// Save the token in fragment
|
// Save the token in fragment
|
||||||
val accessToken = result.data.accessToken
|
val accessToken = result.data.accessToken
|
||||||
sessionManager.saveToken(accessToken)
|
sessionManager.saveToken(accessToken)
|
||||||
|
retrieveFCMToken()
|
||||||
|
|
||||||
Log.d(TAG, "Token saved to SessionManager: $accessToken")
|
Log.d(TAG, "Token saved to SessionManager: $accessToken")
|
||||||
|
|
||||||
// Proceed to Step 3
|
// Proceed to Step 3
|
||||||
@ -279,9 +269,116 @@ class RegisterStep2Fragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
private fun retrieveFCMToken() {
|
||||||
super.onDestroyView()
|
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 = requireContext().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
|
||||||
|
)
|
||||||
|
registerViewModel.sendFcm(tokenFcm)
|
||||||
|
Log.d(TAG, "Sent token fcm: $token")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startResendCooldown() {
|
||||||
|
Log.d(TAG, "startResendCooldown called")
|
||||||
|
|
||||||
|
// Cancel any existing timer first
|
||||||
|
stopTimer()
|
||||||
|
|
||||||
|
timeRemaining = 30
|
||||||
|
isTimerRunning = true
|
||||||
|
binding.tvResendOtp.isEnabled = false
|
||||||
|
binding.tvResendOtp.setTextColor(ContextCompat.getColor(requireContext(), R.color.soft_gray))
|
||||||
|
|
||||||
|
countDownTimer = object : CountDownTimer(30000, 1000) {
|
||||||
|
override fun onTick(millisUntilFinished: Long) {
|
||||||
|
if (!isTimerRunning) {
|
||||||
|
cancel()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
timeRemaining = (millisUntilFinished / 1000).toInt()
|
||||||
|
|
||||||
|
// Check if fragment is still attached before updating UI
|
||||||
|
if (isAdded && _binding != null) {
|
||||||
|
binding.tvTimer.text = "Kirim ulang OTP dalam waktu 00:${String.format("%02d", timeRemaining)}"
|
||||||
|
if (timeRemaining % 5 == 0) {
|
||||||
|
Log.d(TAG, "Cooldown remaining: $timeRemaining seconds")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFinish() {
|
||||||
|
if (!isTimerRunning) return
|
||||||
|
|
||||||
|
Log.d(TAG, "Cooldown finished, enabling resend button")
|
||||||
|
|
||||||
|
// Check if fragment is still attached before updating UI
|
||||||
|
if (isAdded && _binding != null) {
|
||||||
|
binding.tvTimer.text = "Dapat mengirim ulang kode OTP"
|
||||||
|
binding.tvResendOtp.isEnabled = true
|
||||||
|
binding.tvResendOtp.setTextColor(ContextCompat.getColor(requireContext(), R.color.blue1))
|
||||||
|
timeRemaining = 0
|
||||||
|
}
|
||||||
|
isTimerRunning = false
|
||||||
|
}
|
||||||
|
}.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stopTimer() {
|
||||||
|
Log.d(TAG, "stopTimer called")
|
||||||
|
isTimerRunning = false
|
||||||
countDownTimer?.cancel()
|
countDownTimer?.cancel()
|
||||||
|
countDownTimer = null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
Log.d(TAG, "onPause - stopping timer")
|
||||||
|
stopTimer()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStop() {
|
||||||
|
super.onStop()
|
||||||
|
Log.d(TAG, "onStop - stopping timer")
|
||||||
|
stopTimer()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
Log.d(TAG, "onDestroyView - cleaning up")
|
||||||
|
super.onDestroyView()
|
||||||
|
|
||||||
|
// Ensure timer is stopped
|
||||||
|
stopTimer()
|
||||||
|
|
||||||
_binding = null
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDetach() {
|
||||||
|
super.onDetach()
|
||||||
|
Log.d(TAG, "onDetach - final cleanup")
|
||||||
|
stopTimer()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -96,26 +96,21 @@ class RegisterStep3Fragment : Fragment() {
|
|||||||
Log.d(TAG, "Auto-filled name: ${it.name}, phone: ${it.phone}")
|
Log.d(TAG, "Auto-filled name: ${it.name}, phone: ${it.phone}")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up province and city dropdowns
|
|
||||||
setupAutoComplete()
|
setupAutoComplete()
|
||||||
|
|
||||||
setupEdgeToEdge()
|
setupEdgeToEdge()
|
||||||
|
|
||||||
// Set up button listeners
|
// Set up button listeners
|
||||||
binding.btnPrevious.setOnClickListener {
|
binding.btnPrevious.setOnClickListener {
|
||||||
// Go back to the previous step
|
val step2Fragment = RegisterStep2Fragment()
|
||||||
parentFragmentManager.popBackStack()
|
parentFragmentManager.beginTransaction()
|
||||||
|
.replace(R.id.fragment_container, step2Fragment)
|
||||||
|
.commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.btnRegister.setOnClickListener {
|
binding.btnRegister.setOnClickListener {
|
||||||
submitAddress()
|
submitAddress()
|
||||||
}
|
}
|
||||||
|
|
||||||
// If user skips address entry
|
|
||||||
// binding.btnSkip.setOnClickListener {
|
|
||||||
// showRegistrationSuccess()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Observe address submission state
|
// Observe address submission state
|
||||||
observeAddressSubmissionState()
|
observeAddressSubmissionState()
|
||||||
|
|
||||||
@ -503,7 +498,8 @@ class RegisterStep3Fragment : Fragment() {
|
|||||||
|
|
||||||
private fun showRegistrationSuccess() {
|
private fun showRegistrationSuccess() {
|
||||||
// Now we can show the success message for the overall registration process
|
// Now we can show the success message for the overall registration process
|
||||||
Toast.makeText(requireContext(), "Registration completed successfully!", Toast.LENGTH_LONG).show()
|
Toast.makeText(requireContext(), "Berhasil mendaftarkan akun", Toast.LENGTH_LONG).show()
|
||||||
|
sessionManager.clearAll()
|
||||||
|
|
||||||
// Navigate to login screen
|
// Navigate to login screen
|
||||||
startActivity(Intent(requireContext(), LoginActivity::class.java))
|
startActivity(Intent(requireContext(), LoginActivity::class.java))
|
||||||
@ -521,4 +517,5 @@ class RegisterStep3Fragment : Fragment() {
|
|||||||
ViewCompat.setWindowInsetsAnimationCallback(binding.root, null)
|
ViewCompat.setWindowInsetsAnimationCallback(binding.root, null)
|
||||||
_binding = null
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -41,11 +41,16 @@ class CartActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
sessionManager = SessionManager(this)
|
||||||
|
apiService = ApiConfig.getApiService(sessionManager)
|
||||||
|
|
||||||
|
|
||||||
binding = ActivityCartBinding.inflate(layoutInflater)
|
binding = ActivityCartBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
|
||||||
sessionManager = SessionManager(this)
|
if (!sessionManager.isLoggedIn()){
|
||||||
apiService = ApiConfig.getApiService(sessionManager)
|
binding.emptyCart.text = "Silahkan masuk terlebih dahulu"
|
||||||
|
}
|
||||||
|
|
||||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||||
|
|
||||||
@ -118,7 +123,7 @@ class CartActivity : AppCompatActivity() {
|
|||||||
// Start checkout with the prepared items
|
// Start checkout with the prepared items
|
||||||
startCheckoutWithWholesaleInfo(selectedItems)
|
startCheckoutWithWholesaleInfo(selectedItems)
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(this, "Please select items from a single store only", Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, "Pilih produk yang sama dengan toko", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -124,7 +124,7 @@ class ChatActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
if (token.isEmpty()) {
|
if (token.isEmpty()) {
|
||||||
// User not logged in, redirect to login
|
// User not logged in, redirect to login
|
||||||
Toast.makeText(this, "Please login first", Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, "Silahkan masuk terlebih dahulu", Toast.LENGTH_SHORT).show()
|
||||||
startActivity(Intent(this, LoginActivity::class.java))
|
startActivity(Intent(this, LoginActivity::class.java))
|
||||||
finish()
|
finish()
|
||||||
return
|
return
|
||||||
@ -506,7 +506,7 @@ class ChatActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Toast.makeText(this, "Cannot open product details", Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, "Gagal memuat produk", Toast.LENGTH_SHORT).show()
|
||||||
Log.e(TAG, "Error navigating to product detail", e)
|
Log.e(TAG, "Error navigating to product detail", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -622,7 +622,7 @@ class ChatActivity : AppCompatActivity() {
|
|||||||
if (outputFile.exists() && outputFile.length() > 0) {
|
if (outputFile.exists() && outputFile.length() > 0) {
|
||||||
if (outputFile.length() > 5 * 1024 * 1024) {
|
if (outputFile.length() > 5 * 1024 * 1024) {
|
||||||
Log.e(TAG, "File too large: ${outputFile.length()} bytes")
|
Log.e(TAG, "File too large: ${outputFile.length()} bytes")
|
||||||
Toast.makeText(this, "Image too large (max 5MB)", Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, "Gambar terlalu besar. Maksimal 1MB", Toast.LENGTH_SHORT).show()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import android.util.Log
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||||
@ -80,8 +79,10 @@ class ChatListFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Result.Error -> {
|
is Result.Error -> {
|
||||||
binding.tvEmptyChat.visibility = View.VISIBLE
|
// binding.tvEmptyChat.visibility = View.VISIBLE
|
||||||
Toast.makeText(requireContext(), "Failed to load chats", Toast.LENGTH_SHORT).show()
|
binding.progressBarChat.visibility = View.VISIBLE
|
||||||
|
// Toast.makeText(requireContext(), "Failed to load chats", Toast.LENGTH_SHORT).show()
|
||||||
|
Log.e(TAG, "Failed to load chats")
|
||||||
}
|
}
|
||||||
Result.Loading -> {
|
Result.Loading -> {
|
||||||
binding.progressBarChat.visibility = View.VISIBLE
|
binding.progressBarChat.visibility = View.VISIBLE
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import android.util.Log
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
@ -32,6 +33,7 @@ import com.alya.ecommerce_serang.utils.SessionManager
|
|||||||
import com.alya.ecommerce_serang.utils.setLightStatusBar
|
import com.alya.ecommerce_serang.utils.setLightStatusBar
|
||||||
import com.alya.ecommerce_serang.utils.viewmodel.HomeUiState
|
import com.alya.ecommerce_serang.utils.viewmodel.HomeUiState
|
||||||
import com.alya.ecommerce_serang.utils.viewmodel.HomeViewModel
|
import com.alya.ecommerce_serang.utils.viewmodel.HomeViewModel
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
//@AndroidEntryPoint
|
//@AndroidEntryPoint
|
||||||
@ -67,12 +69,10 @@ class HomeFragment : Fragment() {
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
|
||||||
initUi()
|
initUi()
|
||||||
setupRecyclerView()
|
setupRecyclerView()
|
||||||
observeData()
|
observeData()
|
||||||
setupSearchView()
|
setupSearchView()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupRecyclerView() {
|
private fun setupRecyclerView() {
|
||||||
@ -140,24 +140,26 @@ class HomeFragment : Fragment() {
|
|||||||
viewModel.uiState.collect { state ->
|
viewModel.uiState.collect { state ->
|
||||||
when (state) {
|
when (state) {
|
||||||
is HomeUiState.Loading -> {
|
is HomeUiState.Loading -> {
|
||||||
binding.loading.root.isVisible = true
|
binding.loadingAll.root.visibility = View.VISIBLE
|
||||||
binding.error.root.isVisible = false
|
binding.error.root.isVisible = false
|
||||||
binding.home.isVisible = false
|
binding.home.isVisible = false
|
||||||
}
|
}
|
||||||
is HomeUiState.Success -> {
|
is HomeUiState.Success -> {
|
||||||
binding.loading.root.isVisible = false
|
val products = state.products
|
||||||
|
viewModel.loadStoresForProducts(products)
|
||||||
|
delay(2000)
|
||||||
|
binding.loadingAll.root.visibility = View.GONE
|
||||||
binding.error.root.isVisible = false
|
binding.error.root.isVisible = false
|
||||||
binding.home.isVisible = true
|
binding.home.isVisible = true
|
||||||
val products = state.products
|
|
||||||
viewModel.loadStoresForProducts(products) // << add this here
|
|
||||||
|
|
||||||
productAdapter?.updateLimitedProducts(products)
|
productAdapter?.updateLimitedProducts(products)
|
||||||
}
|
}
|
||||||
is HomeUiState.Error -> {
|
is HomeUiState.Error -> {
|
||||||
binding.loading.root.isVisible = false
|
binding.loadingAll.root.visibility = View.GONE
|
||||||
binding.error.root.isVisible = true
|
binding.error.root.isVisible = true
|
||||||
binding.home.isVisible = false
|
binding.home.isVisible = false
|
||||||
binding.error.errorMessage.text = state.message
|
// binding.error.errorMessage.text = state.message
|
||||||
|
Log.e("HomeFragment", "Error load data: ${state.message}")
|
||||||
|
Toast.makeText(requireContext(), "Terjadi kendala. Muat ulang halaman", Toast.LENGTH_SHORT) .show()
|
||||||
binding.error.retryButton.setOnClickListener {
|
binding.error.retryButton.setOnClickListener {
|
||||||
viewModel.retry()
|
viewModel.retry()
|
||||||
}
|
}
|
||||||
@ -166,7 +168,6 @@ class HomeFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
|||||||
@ -7,12 +7,15 @@ import android.view.LayoutInflater
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import com.alya.ecommerce_serang.R
|
||||||
import com.alya.ecommerce_serang.data.api.dto.ProductsItem
|
import com.alya.ecommerce_serang.data.api.dto.ProductsItem
|
||||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||||
import com.alya.ecommerce_serang.data.repository.ProductRepository
|
import com.alya.ecommerce_serang.data.repository.ProductRepository
|
||||||
@ -106,6 +109,11 @@ class SearchHomeFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
val searchText = findViewById<TextView>(androidx.appcompat.R.id.search_src_text)
|
||||||
|
searchText.textSize = 14f // in sp
|
||||||
|
searchText.setHintTextColor(ContextCompat.getColor(context, R.color.black_200))
|
||||||
|
searchText.setTextColor(ContextCompat.getColor(context, R.color.black))
|
||||||
|
|
||||||
if (args.query.isNullOrEmpty()) {
|
if (args.query.isNullOrEmpty()) {
|
||||||
requestFocus()
|
requestFocus()
|
||||||
post {
|
post {
|
||||||
|
|||||||
@ -78,4 +78,4 @@ import com.google.firebase.messaging.RemoteMessage
|
|||||||
val notificationId = System.currentTimeMillis().toInt()
|
val notificationId = System.currentTimeMillis().toInt()
|
||||||
notificationManager.notify(notificationId, notificationBuilder.build())
|
notificationManager.notify(notificationId, notificationBuilder.build())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -154,8 +154,18 @@ class CheckoutActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
// Observe address details
|
// Observe address details
|
||||||
viewModel.addressDetails.observe(this) { address ->
|
viewModel.addressDetails.observe(this) { address ->
|
||||||
binding.tvPlacesAddress.text = address?.recipient
|
if (address != null) {
|
||||||
binding.tvAddress.text = "${address?.street}, ${address?.subdistrict}"
|
// Show selected address
|
||||||
|
binding.containerEmptyAddress.visibility = View.GONE
|
||||||
|
binding.containerAddress.visibility = View.VISIBLE
|
||||||
|
|
||||||
|
binding.tvPlacesAddress.text = address.recipient
|
||||||
|
binding.tvAddress.text = "${address.street}, ${address.subdistrict}"
|
||||||
|
} else {
|
||||||
|
// Show empty address state
|
||||||
|
binding.containerEmptyAddress.visibility = View.VISIBLE
|
||||||
|
binding.containerAddress.visibility = View.GONE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.availablePaymentMethods.observe(this) { paymentMethods ->
|
viewModel.availablePaymentMethods.observe(this) { paymentMethods ->
|
||||||
@ -172,9 +182,7 @@ class CheckoutActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
// Update the adapter ONLY if it exists
|
// Update the adapter ONLY if it exists
|
||||||
paymentAdapter?.let { adapter ->
|
paymentAdapter?.let { adapter ->
|
||||||
// This line was causing issues - using setSelectedPayment instead of setSelectedPaymentName
|
|
||||||
adapter.setSelectedPaymentId(selectedPayment.id)
|
adapter.setSelectedPaymentId(selectedPayment.id)
|
||||||
|
|
||||||
Log.d("CheckoutActivity", "Updated adapter with selected payment: ${selectedPayment.id}")
|
Log.d("CheckoutActivity", "Updated adapter with selected payment: ${selectedPayment.id}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -183,20 +191,20 @@ class CheckoutActivity : AppCompatActivity() {
|
|||||||
// Observe loading state
|
// Observe loading state
|
||||||
viewModel.isLoading.observe(this) { isLoading ->
|
viewModel.isLoading.observe(this) { isLoading ->
|
||||||
binding.btnPay.isEnabled = !isLoading
|
binding.btnPay.isEnabled = !isLoading
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Observe error messages
|
// Observe error messages
|
||||||
viewModel.errorMessage.observe(this) { message ->
|
viewModel.errorMessage.observe(this) { message ->
|
||||||
if (message.isNotEmpty()) {
|
if (message.isNotEmpty()) {
|
||||||
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, "Terdapat kendala di pemesanan", Toast.LENGTH_SHORT).show()
|
||||||
|
Log.e("CheckoutActivity", "Error from errorMessage: $message")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Observe order creation
|
// Observe order creation
|
||||||
viewModel.orderCreated.observe(this) { created ->
|
viewModel.orderCreated.observe(this) { created ->
|
||||||
if (created) {
|
if (created) {
|
||||||
Toast.makeText(this, "Order successfully created!", Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, "Berhasil membuat pesanan", Toast.LENGTH_SHORT).show()
|
||||||
setResult(RESULT_OK)
|
setResult(RESULT_OK)
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
@ -206,10 +214,17 @@ class CheckoutActivity : AppCompatActivity() {
|
|||||||
private fun setupPaymentMethodsRecyclerView(paymentMethods: List<DetailPaymentItem>) {
|
private fun setupPaymentMethodsRecyclerView(paymentMethods: List<DetailPaymentItem>) {
|
||||||
if (paymentMethods.isEmpty()) {
|
if (paymentMethods.isEmpty()) {
|
||||||
Log.e("CheckoutActivity", "Payment methods list is empty")
|
Log.e("CheckoutActivity", "Payment methods list is empty")
|
||||||
Toast.makeText(this, "No payment methods available", Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, "Tidak ditemukan metode pembayaran", Toast.LENGTH_SHORT).show()
|
||||||
|
|
||||||
|
// Show empty payment state
|
||||||
|
binding.containerEmptyPayment.visibility = View.VISIBLE
|
||||||
|
binding.rvPaymentInfo.visibility = View.GONE
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binding.containerEmptyPayment.visibility = View.GONE
|
||||||
|
binding.rvPaymentInfo.visibility = View.VISIBLE
|
||||||
|
|
||||||
// Debug logging
|
// Debug logging
|
||||||
Log.d("CheckoutActivity", "Setting up payment methods: ${paymentMethods.size} methods available")
|
Log.d("CheckoutActivity", "Setting up payment methods: ${paymentMethods.size} methods available")
|
||||||
|
|
||||||
@ -266,6 +281,16 @@ class CheckoutActivity : AppCompatActivity() {
|
|||||||
this.adapter = adapter
|
this.adapter = adapter
|
||||||
isNestedScrollingEnabled = false
|
isNestedScrollingEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if (checkoutData.cartItems.isEmpty()) {
|
||||||
|
// // Show empty products state
|
||||||
|
// binding.containerEmptyProducts.visibility = View.VISIBLE
|
||||||
|
// binding.rvProductItems.visibility = View.GONE
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
binding.containerEmptyProducts.visibility = View.GONE
|
||||||
|
binding.rvProductItems.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateOrderSummary() {
|
private fun updateOrderSummary() {
|
||||||
@ -290,7 +315,8 @@ class CheckoutActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
private fun updateShippingUI(shipName: String, shipService: String, shipEtd: String, shipPrice: Int) {
|
private fun updateShippingUI(shipName: String, shipService: String, shipEtd: String, shipPrice: Int) {
|
||||||
if (shipName.isNotEmpty() && shipService.isNotEmpty()) {
|
if (shipName.isNotEmpty() && shipService.isNotEmpty()) {
|
||||||
// Display shipping name and service in one line
|
// Hide empty state and show selected shipping
|
||||||
|
binding.containerEmptyShipping.visibility = View.GONE
|
||||||
binding.cardShipment.visibility = View.VISIBLE
|
binding.cardShipment.visibility = View.VISIBLE
|
||||||
|
|
||||||
binding.tvCourierName.text = "$shipName $shipService"
|
binding.tvCourierName.text = "$shipName $shipService"
|
||||||
@ -298,6 +324,8 @@ class CheckoutActivity : AppCompatActivity() {
|
|||||||
binding.tvShippingPrice.text = formatCurrency(shipPrice.toDouble())
|
binding.tvShippingPrice.text = formatCurrency(shipPrice.toDouble())
|
||||||
binding.rbJne.isChecked = true
|
binding.rbJne.isChecked = true
|
||||||
} else {
|
} else {
|
||||||
|
// Show empty shipping state
|
||||||
|
binding.containerEmptyShipping.visibility = View.VISIBLE
|
||||||
binding.cardShipment.visibility = View.GONE
|
binding.cardShipment.visibility = View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -310,10 +338,10 @@ class CheckoutActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Shipping method selection
|
// Shipping method selection
|
||||||
binding.layoutShippingMethod.setOnClickListener {
|
binding.tvShippingOption.setOnClickListener {
|
||||||
val addressId = viewModel.addressDetails.value?.id ?: 0
|
val addressId = viewModel.addressDetails.value?.id ?: 0
|
||||||
if (addressId <= 0) {
|
if (addressId <= 0) {
|
||||||
Toast.makeText(this, "Please select delivery address first", Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, "Silahkan pilih alamat dahulu", Toast.LENGTH_SHORT).show()
|
||||||
return@setOnClickListener
|
return@setOnClickListener
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,7 +391,7 @@ class CheckoutActivity : AppCompatActivity() {
|
|||||||
viewModel.setSelectedAddress(addressId)
|
viewModel.setSelectedAddress(addressId)
|
||||||
|
|
||||||
// You might want to show a toast or some UI feedback
|
// You might want to show a toast or some UI feedback
|
||||||
Toast.makeText(this, "Address selected successfully", Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, "Berhasil memilih alamat", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -145,6 +145,8 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
|
|||||||
isReseller = isWholesaleMap.any { it.value }
|
isReseller = isWholesaleMap.any { it.value }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Log.d(TAG, "Cek is reseller: ${orderRequest.isReseller}")
|
||||||
|
|
||||||
_checkoutData.value = CheckoutData(
|
_checkoutData.value = CheckoutData(
|
||||||
orderRequest = orderRequest,
|
orderRequest = orderRequest,
|
||||||
productName = matchingItems.first().productName,
|
productName = matchingItems.first().productName,
|
||||||
@ -378,6 +380,7 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
|
|||||||
} else {
|
} else {
|
||||||
// For Cart checkout, use the standard order endpoint
|
// For Cart checkout, use the standard order endpoint
|
||||||
val cartRequest = data.orderRequest as OrderRequest
|
val cartRequest = data.orderRequest as OrderRequest
|
||||||
|
Log.d(TAG, "data: ${cartRequest.cartItemId}")
|
||||||
repository.createOrder(cartRequest)
|
repository.createOrder(cartRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -67,7 +67,7 @@ class ShippingActivity : AppCompatActivity() {
|
|||||||
// Validate required information
|
// Validate required information
|
||||||
if (addressId <= 0 || productId <= 0) {
|
if (addressId <= 0 || productId <= 0) {
|
||||||
Log.e(TAG, "Missing required shipping information: addressId=$addressId, productId=$productId")
|
Log.e(TAG, "Missing required shipping information: addressId=$addressId, productId=$productId")
|
||||||
Toast.makeText(this, "Missing required shipping information", Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, "Gagal memuat pengiriman", Toast.LENGTH_SHORT).show()
|
||||||
finish()
|
finish()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -52,12 +52,12 @@ class AddressActivity : AppCompatActivity() {
|
|||||||
windowInsets
|
windowInsets
|
||||||
}
|
}
|
||||||
|
|
||||||
|
viewModel.fetchAddresses()
|
||||||
|
|
||||||
setupToolbar()
|
setupToolbar()
|
||||||
setupRecyclerView()
|
setupRecyclerView()
|
||||||
setupObservers()
|
setupObservers()
|
||||||
|
|
||||||
viewModel.fetchAddresses()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -126,6 +126,11 @@ class AddressActivity : AppCompatActivity() {
|
|||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
viewModel.fetchAddresses()
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val EXTRA_ADDRESS_ID = "extra_address_id"
|
const val EXTRA_ADDRESS_ID = "extra_address_id"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import android.content.Context
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
import android.widget.Spinner
|
import android.widget.Spinner
|
||||||
|
import com.alya.ecommerce_serang.R
|
||||||
import com.alya.ecommerce_serang.data.api.response.customer.order.CitiesItem
|
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.api.response.customer.order.ProvincesItem
|
||||||
import com.alya.ecommerce_serang.data.api.response.customer.order.SubdistrictsItem
|
import com.alya.ecommerce_serang.data.api.response.customer.order.SubdistrictsItem
|
||||||
@ -134,23 +135,8 @@ class BankAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun loadHardcodedData() {
|
private fun loadHardcodedData() {
|
||||||
val defaultBanks = listOf(
|
val bankNames = context.resources.getStringArray(R.array.bank_names)
|
||||||
BankItem("Bank Mandiri", "008", "PT Bank Mandiri (Persero) Tbk"),
|
val defaultBanks = bankNames.map { BankItem(bankName = it) }
|
||||||
BankItem("Bank BRI", "002", "PT Bank Rakyat Indonesia (Persero) Tbk"),
|
|
||||||
BankItem("Bank BCA", "014", "PT Bank Central Asia Tbk"),
|
|
||||||
BankItem("Bank BNI", "009", "PT Bank Negara Indonesia (Persero) Tbk"),
|
|
||||||
BankItem("Bank BTN", "200", "PT Bank Tabungan Negara (Persero) Tbk"),
|
|
||||||
BankItem("Bank CIMB Niaga", "022", "PT Bank CIMB Niaga Tbk"),
|
|
||||||
BankItem("Bank Danamon", "011", "PT Bank Danamon Indonesia Tbk"),
|
|
||||||
BankItem("Bank Permata", "013", "PT Bank Permata Tbk"),
|
|
||||||
BankItem("Bank OCBC NISP", "028", "PT Bank OCBC NISP Tbk"),
|
|
||||||
BankItem("Bank Maybank", "016", "PT Bank Maybank Indonesia Tbk"),
|
|
||||||
BankItem("Bank Panin", "019", "PT Bank Panin Dubai Syariah Tbk"),
|
|
||||||
BankItem("Bank UOB", "023", "PT Bank UOB Indonesia"),
|
|
||||||
BankItem("Bank Mega", "426", "PT Bank Mega Tbk"),
|
|
||||||
BankItem("Bank Bukopin", "441", "PT Bank Bukopin Tbk"),
|
|
||||||
BankItem("Bank BJB", "110", "PT Bank Pembangunan Daerah Jawa Barat dan Banten Tbk")
|
|
||||||
)
|
|
||||||
updateData(defaultBanks)
|
updateData(defaultBanks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -61,8 +61,8 @@ class AddEvidencePaymentActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val paymentMethods = arrayOf(
|
private val paymentMethods = arrayOf(
|
||||||
|
"Pilih Metode Pembayaran",
|
||||||
"Transfer Bank",
|
"Transfer Bank",
|
||||||
"QRIS",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// private val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
|
// private val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
|
||||||
@ -122,7 +122,6 @@ class AddEvidencePaymentActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "ERROR in AddEvidencePaymentActivity onCreate: ${e.message}", e)
|
Log.e(TAG, "ERROR in AddEvidencePaymentActivity onCreate: ${e.message}", e)
|
||||||
Toast.makeText(this, "Error: ${e.message}", Toast.LENGTH_LONG).show()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,7 +287,7 @@ class AddEvidencePaymentActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Error handling selected image", e)
|
Log.e(TAG, "Error handling selected image", e)
|
||||||
Toast.makeText(this, "Error: ${e.message}", Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, "Terjadi kendala", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,10 +313,10 @@ class AddEvidencePaymentActivity : AppCompatActivity() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (binding.spinnerPaymentMethod.selectedItemPosition == 0) {
|
// if (binding.spinnerPaymentMethod.selectedItemPosition == 0) {
|
||||||
Toast.makeText(this, "Silahkan pilih metode pembayaran", Toast.LENGTH_SHORT).show()
|
// Toast.makeText(this, "Silahkan pilih metode pembayaran", Toast.LENGTH_SHORT).show()
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
binding.etAccountNumber.visibility = View.GONE
|
binding.etAccountNumber.visibility = View.GONE
|
||||||
|
|
||||||
// if (binding.etAccountNumber.text.toString().trim().isEmpty()) {
|
// if (binding.etAccountNumber.text.toString().trim().isEmpty()) {
|
||||||
@ -325,10 +324,10 @@ class AddEvidencePaymentActivity : AppCompatActivity() {
|
|||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if (binding.tvPaymentDate.text.toString() == "Pilih tanggal") {
|
// if (binding.tvPaymentDate.text.toString() == "Pilih tanggal") {
|
||||||
Toast.makeText(this, "Silahkan pilih tanggal pembayaran", Toast.LENGTH_SHORT).show()
|
// Toast.makeText(this, "Silahkan pilih tanggal pembayaran", Toast.LENGTH_SHORT).show()
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
// All validations passed, proceed with upload
|
// All validations passed, proceed with upload
|
||||||
uploadPaymentProof()
|
uploadPaymentProof()
|
||||||
@ -367,7 +366,7 @@ class AddEvidencePaymentActivity : AppCompatActivity() {
|
|||||||
viewModel.uploadPaymentProof(request)
|
viewModel.uploadPaymentProof(request)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Error creating upload request: ${e.message}", e)
|
Log.e(TAG, "Error creating upload request: ${e.message}", e)
|
||||||
Toast.makeText(this, "Error preparing upload: ${e.message}", Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, "Gagal mengunggah foto", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -160,7 +160,8 @@ class PaymentActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
viewModel.error.observe(this) { error ->
|
viewModel.error.observe(this) { error ->
|
||||||
if (error.isNotEmpty()) {
|
if (error.isNotEmpty()) {
|
||||||
Toast.makeText(this, error, Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, "Gagal melakukan pembayaran", Toast.LENGTH_SHORT).show()
|
||||||
|
Log.e(TAG, "Failed payment: $error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,7 +27,6 @@ class HistoryActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityHistoryBinding.inflate(layoutInflater)
|
binding = ActivityHistoryBinding.inflate(layoutInflater)
|
||||||
|
|||||||
@ -37,9 +37,6 @@ class HistoryViewModel(private val repository: OrderRepository) : ViewModel() {
|
|||||||
private const val TAG = "HistoryViewModel"
|
private const val TAG = "HistoryViewModel"
|
||||||
}
|
}
|
||||||
|
|
||||||
// private val _orders = MutableLiveData<ViewState<List<OrdersItem>>>()
|
|
||||||
// val orders: LiveData<ViewState<List<OrdersItem>>> = _orders
|
|
||||||
|
|
||||||
private val _orderCompletionStatus = MutableLiveData<Result<CompletedOrderResponse>>()
|
private val _orderCompletionStatus = MutableLiveData<Result<CompletedOrderResponse>>()
|
||||||
val orderCompletionStatus: LiveData<Result<CompletedOrderResponse>> = _orderCompletionStatus
|
val orderCompletionStatus: LiveData<Result<CompletedOrderResponse>> = _orderCompletionStatus
|
||||||
|
|
||||||
@ -113,83 +110,6 @@ class HistoryViewModel(private val repository: OrderRepository) : ViewModel() {
|
|||||||
ViewState.Loading // ② initial value, still fine
|
ViewState.Loading // ② initial value, still fine
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
// fun getOrderList(status: String) {
|
|
||||||
// _orders.value = ViewState.Loading
|
|
||||||
// viewModelScope.launch {
|
|
||||||
// try {
|
|
||||||
// if (status == "all") {
|
|
||||||
// // Get all orders by combining all statuses
|
|
||||||
// getAllOrdersCombined()
|
|
||||||
// } else {
|
|
||||||
// // Get orders for specific status
|
|
||||||
// when (val result = repository.getOrderList(status)) {
|
|
||||||
// is Result.Success -> {
|
|
||||||
// _orders.value = ViewState.Success(result.data.orders)
|
|
||||||
// Log.d(TAG, "Orders loaded successfully: ${result.data.orders.size} items")
|
|
||||||
// }
|
|
||||||
// is Result.Error -> {
|
|
||||||
// _orders.value = ViewState.Error(result.exception.message ?: "Unknown error occurred")
|
|
||||||
// Log.e(TAG, "Error loading orders", result.exception)
|
|
||||||
// }
|
|
||||||
// is Result.Loading -> {
|
|
||||||
// // Keep loading state
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } catch (e: Exception) {
|
|
||||||
// _orders.value = ViewState.Error("An unexpected error occurred: ${e.message}")
|
|
||||||
// Log.e(TAG, "Exception in getOrderList", e)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private suspend fun getAllOrdersCombined() {
|
|
||||||
// try {
|
|
||||||
// val allStatuses = listOf("unpaid", "paid", "processed", "shipped", "completed", "canceled")
|
|
||||||
// val allOrders = mutableListOf<OrdersItem>()
|
|
||||||
//
|
|
||||||
// // Use coroutineScope to allow launching async blocks
|
|
||||||
// coroutineScope {
|
|
||||||
// val deferreds = allStatuses.map { status ->
|
|
||||||
// async {
|
|
||||||
// when (val result = repository.getOrderList(status)) {
|
|
||||||
// is Result.Success -> {
|
|
||||||
// // Tag each order with the status it was fetched from
|
|
||||||
// result.data.orders.onEach { it.displayStatus = status }
|
|
||||||
// }
|
|
||||||
// is Result.Error -> {
|
|
||||||
// Log.e(TAG, "Error loading orders for status $status", result.exception)
|
|
||||||
// emptyList<OrdersItem>()
|
|
||||||
// }
|
|
||||||
// is Result.Loading -> emptyList<OrdersItem>()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Await all results and combine
|
|
||||||
// deferreds.awaitAll().forEach { orders ->
|
|
||||||
// allOrders.addAll(orders)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Sort orders
|
|
||||||
// val sortedOrders = allOrders.sortedByDescending { order ->
|
|
||||||
// try {
|
|
||||||
// SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault()).parse(order.createdAt)
|
|
||||||
// } catch (e: Exception) {
|
|
||||||
// null
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// _orders.value = ViewState.Success(sortedOrders)
|
|
||||||
// Log.d(TAG, "All orders loaded successfully: ${sortedOrders.size} items")
|
|
||||||
//
|
|
||||||
// } catch (e: Exception) {
|
|
||||||
// _orders.value = ViewState.Error("An unexpected error occurred: ${e.message}")
|
|
||||||
// Log.e(TAG, "Exception in getAllOrdersCombined", e)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
private suspend fun getAllOrdersCombined(): ViewState<List<OrdersItem>> = try {
|
private suspend fun getAllOrdersCombined(): ViewState<List<OrdersItem>> = try {
|
||||||
val statuses = listOf("unpaid", "paid", "processed", "shipped", "completed", "canceled")
|
val statuses = listOf("unpaid", "paid", "processed", "shipped", "completed", "canceled")
|
||||||
|
|
||||||
@ -292,11 +212,6 @@ class HistoryViewModel(private val repository: OrderRepository) : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fun refreshOrders(status: String = "all") {
|
|
||||||
// Log.d(TAG, "Refreshing orders with status: $status")
|
|
||||||
// // Don't set Loading here if you want to show current data while refreshing
|
|
||||||
// getOrderList(status)
|
|
||||||
// }
|
|
||||||
|
|
||||||
fun updateStatus(status: String, forceRefresh: Boolean = false) {
|
fun updateStatus(status: String, forceRefresh: Boolean = false) {
|
||||||
Log.d(TAG, "↪️ updateStatus(status = $status, forceRefresh = $forceRefresh)")
|
Log.d(TAG, "↪️ updateStatus(status = $status, forceRefresh = $forceRefresh)")
|
||||||
|
|||||||
@ -200,10 +200,6 @@ class OrderHistoryAdapter(
|
|||||||
// viewModel.refreshOrders()
|
// viewModel.refreshOrders()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// deadlineDate.apply {
|
|
||||||
// visibility = View.VISIBLE
|
|
||||||
// text = formatDatePay(order.updatedAt)
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
"processed" -> {
|
"processed" -> {
|
||||||
// Untuk status processed, tampilkan "Hubungi Penjual"
|
// Untuk status processed, tampilkan "Hubungi Penjual"
|
||||||
@ -215,15 +211,6 @@ class OrderHistoryAdapter(
|
|||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
text = itemView.context.getString(R.string.dl_processed)
|
text = itemView.context.getString(R.string.dl_processed)
|
||||||
}
|
}
|
||||||
// gabisa complaint
|
|
||||||
// btnLeft.apply {
|
|
||||||
// visibility = View.VISIBLE
|
|
||||||
// text = itemView.context.getString(R.string.canceled_order_btn)
|
|
||||||
// setOnClickListener {
|
|
||||||
// showCancelOrderDialog(order.orderId.toString())
|
|
||||||
// viewModel.refreshOrders()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
"shipped" -> {
|
"shipped" -> {
|
||||||
// Untuk status shipped, tampilkan "Lacak Pengiriman" dan "Terima Barang"
|
// Untuk status shipped, tampilkan "Lacak Pengiriman" dan "Terima Barang"
|
||||||
@ -517,14 +504,14 @@ class OrderHistoryAdapter(
|
|||||||
} else {
|
} else {
|
||||||
// Log error and show a Toast instead if we can't get a FragmentManager
|
// 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")
|
Log.e("OrderHistoryAdapter", "Cannot show bottom sheet: Context is not a FragmentActivity")
|
||||||
Toast.makeText(context, "Cannot show cancel order dialog", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "Terjadi kendala di batalkan pesanan", Toast.LENGTH_SHORT).show()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// Log error and show a Toast instead if we can't get a FragmentManager
|
// 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")
|
Log.e("OrderHistoryAdapter", "Cannot show bottom sheet: Context is not a FragmentActivity")
|
||||||
Toast.makeText(context, "Cannot show cancel order dialog", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "Terjadi kendala di batalkan pesanan", Toast.LENGTH_SHORT).show()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -535,7 +522,7 @@ class OrderHistoryAdapter(
|
|||||||
onOrderCancelled = {
|
onOrderCancelled = {
|
||||||
callbacks.onOrderCancelled(orderId.toString(), true, "Order cancelled successfully")
|
callbacks.onOrderCancelled(orderId.toString(), true, "Order cancelled successfully")
|
||||||
// Show a success message
|
// Show a success message
|
||||||
Toast.makeText(context, "Order cancelled successfully", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "Pesanan berhasil dibatalkan", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -547,18 +534,10 @@ class OrderHistoryAdapter(
|
|||||||
// Use ViewModel to fetch order details
|
// Use ViewModel to fetch order details
|
||||||
viewModel.getOrderDetails(order.orderId)
|
viewModel.getOrderDetails(order.orderId)
|
||||||
|
|
||||||
// Create loading dialog
|
|
||||||
// val loadingDialog = Dialog(itemView.context).apply {
|
|
||||||
// requestWindowFeature(Window.FEATURE_NO_TITLE)
|
|
||||||
// setContentView(R.layout.dialog_loading)
|
|
||||||
// window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
|
||||||
// setCancelable(false)
|
|
||||||
// }
|
|
||||||
// loadingDialog.show()
|
|
||||||
|
|
||||||
viewModel.error.observe(itemView.findViewTreeLifecycleOwner()!!) { errorMsg ->
|
viewModel.error.observe(itemView.findViewTreeLifecycleOwner()!!) { errorMsg ->
|
||||||
if (!errorMsg.isNullOrEmpty()) {
|
if (!errorMsg.isNullOrEmpty()) {
|
||||||
Toast.makeText(itemView.context, errorMsg, Toast.LENGTH_SHORT).show()
|
Log.e("OrderHistoryAdapter", "Error $errorMsg")
|
||||||
|
Toast.makeText(itemView.context, "Terdapat kendala di tambah ulasan", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -594,7 +573,7 @@ class OrderHistoryAdapter(
|
|||||||
} else {
|
} else {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
itemView.context,
|
itemView.context,
|
||||||
"No items to review",
|
"Tidak ada produk untuk direview",
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,8 +44,6 @@ class OrderHistoryFragment : Fragment() {
|
|||||||
sessionManager = SessionManager(requireContext())
|
sessionManager = SessionManager(requireContext())
|
||||||
|
|
||||||
setupViewPager()
|
setupViewPager()
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupViewPager() {
|
private fun setupViewPager() {
|
||||||
@ -67,17 +65,25 @@ class OrderHistoryFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}.attach()
|
}.attach()
|
||||||
|
|
||||||
|
statusPage()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun statusPage(){
|
||||||
binding.viewPager.registerOnPageChangeCallback(
|
binding.viewPager.registerOnPageChangeCallback(
|
||||||
object : ViewPager2.OnPageChangeCallback() {
|
object : ViewPager2.OnPageChangeCallback() {
|
||||||
override fun onPageSelected(position: Int) {
|
override fun onPageSelected(position: Int) {
|
||||||
val status = viewPagerAdapter.orderStatuses[position]
|
val status = viewPagerAdapter.orderStatuses[position]
|
||||||
/* setStatus() is the API we added earlier; TRUE → always re‑query */
|
|
||||||
historyVm.updateStatus(status, forceRefresh = true)
|
historyVm.updateStatus(status, forceRefresh = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
statusPage()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
_binding = null
|
_binding = null
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package com.alya.ecommerce_serang.ui.order.history
|
|||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
@ -84,8 +85,6 @@ class OrderListFragment : Fragment(), OrderHistoryAdapter.OrderActionCallbacks {
|
|||||||
setupRecyclerView()
|
setupRecyclerView()
|
||||||
observeOrderList()
|
observeOrderList()
|
||||||
observeViewModel()
|
observeViewModel()
|
||||||
// observeOrderCompletionStatus()
|
|
||||||
// loadOrders()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupRecyclerView() {
|
private fun setupRecyclerView() {
|
||||||
@ -106,31 +105,6 @@ class OrderListFragment : Fragment(), OrderHistoryAdapter.OrderActionCallbacks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun observeOrderList() {
|
private fun observeOrderList() {
|
||||||
// Now we only need to observe one LiveData for all cases
|
|
||||||
// viewModel.orders.observe(viewLifecycleOwner) { result ->
|
|
||||||
// when (result) {
|
|
||||||
// is ViewState.Success -> {
|
|
||||||
// binding.progressBar.visibility = View.GONE
|
|
||||||
//
|
|
||||||
// if (result.data.isNullOrEmpty()) {
|
|
||||||
// binding.tvEmptyState.visibility = View.VISIBLE
|
|
||||||
// binding.rvOrders.visibility = View.GONE
|
|
||||||
// } else {
|
|
||||||
// binding.tvEmptyState.visibility = View.GONE
|
|
||||||
// binding.rvOrders.visibility = View.VISIBLE
|
|
||||||
// orderAdapter.submitList(result.data)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// is ViewState.Error -> {
|
|
||||||
// binding.progressBar.visibility = View.GONE
|
|
||||||
// binding.tvEmptyState.visibility = View.VISIBLE
|
|
||||||
// Toast.makeText(requireContext(), result.message, Toast.LENGTH_SHORT).show()
|
|
||||||
// }
|
|
||||||
// is ViewState.Loading -> {
|
|
||||||
// binding.progressBar.visibility = View.VISIBLE
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
|
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
|
||||||
viewModel.orders.collect { state ->
|
viewModel.orders.collect { state ->
|
||||||
when (state) {
|
when (state) {
|
||||||
@ -141,7 +115,7 @@ class OrderListFragment : Fragment(), OrderHistoryAdapter.OrderActionCallbacks {
|
|||||||
binding.progressBar.isVisible = false
|
binding.progressBar.isVisible = false
|
||||||
binding.tvEmptyState.isVisible = true
|
binding.tvEmptyState.isVisible = true
|
||||||
binding.rvOrders.isVisible = false
|
binding.rvOrders.isVisible = false
|
||||||
Toast.makeText(requireContext(), state.message, Toast.LENGTH_SHORT).show()
|
Log.e("OrderListFragment", "Error in order list: ${state.message}")
|
||||||
}
|
}
|
||||||
is ViewState.Success -> {
|
is ViewState.Success -> {
|
||||||
binding.progressBar.isVisible = false
|
binding.progressBar.isVisible = false
|
||||||
@ -157,47 +131,19 @@ class OrderListFragment : Fragment(), OrderHistoryAdapter.OrderActionCallbacks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun observeViewModel() {
|
private fun observeViewModel() {
|
||||||
// Observe order completion
|
|
||||||
// viewModel.orderCompletionStatus.observe(viewLifecycleOwner) { result ->
|
|
||||||
// when (result) {
|
|
||||||
// is Result.Success -> {
|
|
||||||
// Toast.makeText(requireContext(), "Order completed successfully!", Toast.LENGTH_SHORT).show()
|
|
||||||
//// loadOrders() // Refresh here
|
|
||||||
// }
|
|
||||||
// is Result.Error -> {
|
|
||||||
// Toast.makeText(requireContext(), "Failed: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
|
||||||
// }
|
|
||||||
// is Result.Loading -> {
|
|
||||||
// // Show loading if needed
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Observe cancel order status
|
|
||||||
// viewModel.cancelOrderStatus.observe(viewLifecycleOwner) { result ->
|
|
||||||
// when (result) {
|
|
||||||
// is Result.Success -> {
|
|
||||||
// Toast.makeText(requireContext(), "Order cancelled successfully!", Toast.LENGTH_SHORT).show()
|
|
||||||
// loadOrders() // Refresh here
|
|
||||||
// }
|
|
||||||
// is Result.Error -> {
|
|
||||||
// Toast.makeText(requireContext(), "Failed to cancel: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
|
||||||
// }
|
|
||||||
// is Result.Loading -> {
|
|
||||||
// // Show loading if needed
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
viewModel.orderCompletionStatus.observe(viewLifecycleOwner) { result ->
|
viewModel.orderCompletionStatus.observe(viewLifecycleOwner) { result ->
|
||||||
when (result) {
|
when (result) {
|
||||||
is Result.Success -> {
|
is Result.Success -> {
|
||||||
Toast.makeText(requireContext(),
|
Toast.makeText(requireContext(),
|
||||||
"Order completed!", Toast.LENGTH_SHORT).show()
|
"Pesanan Selesai", Toast.LENGTH_SHORT).show()
|
||||||
|
Log.d("OrderListFragment", "Order selesai")
|
||||||
viewModel.updateStatus(status, forceRefresh = true)
|
viewModel.updateStatus(status, forceRefresh = true)
|
||||||
}
|
}
|
||||||
is Result.Error ->
|
is Result.Error ->
|
||||||
Toast.makeText(requireContext(),
|
// Toast.makeText(requireContext(),
|
||||||
"Failed: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
// "Failed: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
||||||
|
Log.e("OrderListFragment", "Failed: ${result.exception.message}")
|
||||||
|
|
||||||
else -> { /* Loading → no UI change */ }
|
else -> { /* Loading → no UI change */ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -206,31 +152,19 @@ class OrderListFragment : Fragment(), OrderHistoryAdapter.OrderActionCallbacks {
|
|||||||
when (result) {
|
when (result) {
|
||||||
is Result.Success -> {
|
is Result.Success -> {
|
||||||
Toast.makeText(requireContext(),
|
Toast.makeText(requireContext(),
|
||||||
"Order cancelled!", Toast.LENGTH_SHORT).show()
|
"Pesanan Dibatalkan", Toast.LENGTH_SHORT).show()
|
||||||
|
Log.d("OrderListFragment", "Order dibatalkan")
|
||||||
viewModel.updateStatus(status, forceRefresh = true)
|
viewModel.updateStatus(status, forceRefresh = true)
|
||||||
}
|
}
|
||||||
is Result.Error ->
|
is Result.Error ->
|
||||||
Toast.makeText(requireContext(),
|
Log.e("OrderListFragment", "Failed: ${result.exception.message}")
|
||||||
"Failed: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
// Toast.makeText(requireContext(),
|
||||||
|
// "Failed: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
||||||
else -> { /* Loading */ }
|
else -> { /* Loading */ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// private fun loadOrders() {
|
|
||||||
// // Simple - just call getOrderList for any status including "all"
|
|
||||||
// viewModel.getOrderList(status)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private val detailOrderLauncher = registerForActivityResult(
|
|
||||||
// ActivityResultContracts.StartActivityForResult()
|
|
||||||
// ) { result ->
|
|
||||||
// if (result.resultCode == Activity.RESULT_OK) {
|
|
||||||
// // Refresh order list when returning with OK result
|
|
||||||
//// loadOrders()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
private fun navigateToOrderDetail(order: OrdersItem) {
|
private fun navigateToOrderDetail(order: OrdersItem) {
|
||||||
val intent = Intent(requireContext(), DetailOrderStatusActivity::class.java).apply {
|
val intent = Intent(requireContext(), DetailOrderStatusActivity::class.java).apply {
|
||||||
putExtra("ORDER_ID", order.orderId)
|
putExtra("ORDER_ID", order.orderId)
|
||||||
@ -239,11 +173,10 @@ class OrderListFragment : Fragment(), OrderHistoryAdapter.OrderActionCallbacks {
|
|||||||
detailOrderLauncher.launch(intent)
|
detailOrderLauncher.launch(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
override fun onOrderCancelled(orderId: String, success: Boolean, message: String) {
|
override fun onOrderCancelled(orderId: String, success: Boolean, message: String) {
|
||||||
if (success) {
|
if (success) {
|
||||||
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
|
Toast.makeText(requireContext(), "Berhasil batalkan pesanan", Toast.LENGTH_SHORT).show()
|
||||||
|
Log.d("OrderListFragment", "Order cancel success: $message")
|
||||||
// loadOrders() // Refresh the list
|
// loadOrders() // Refresh the list
|
||||||
if (success) viewModel.updateStatus(status, forceRefresh = true)
|
if (success) viewModel.updateStatus(status, forceRefresh = true)
|
||||||
|
|
||||||
@ -254,11 +187,13 @@ class OrderListFragment : Fragment(), OrderHistoryAdapter.OrderActionCallbacks {
|
|||||||
|
|
||||||
override fun onOrderCompleted(orderId: Int, success: Boolean, message: String) {
|
override fun onOrderCompleted(orderId: Int, success: Boolean, message: String) {
|
||||||
if (success) {
|
if (success) {
|
||||||
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
|
Toast.makeText(requireContext(), "Pesanan selesai", Toast.LENGTH_SHORT).show()
|
||||||
|
Log.d("OrderListFragment", "Pesanan selesai: $message")
|
||||||
// loadOrders() // Refresh the list
|
// loadOrders() // Refresh the list
|
||||||
if (success) viewModel.updateStatus(status, forceRefresh = true)
|
if (success) viewModel.updateStatus(status, forceRefresh = true)
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
|
Log.e("OrderListFragment", "Error Order Complete: $message")
|
||||||
|
Toast.makeText(requireContext(), "Terdapat kendala di pesanan selesai", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,20 +206,8 @@ class OrderListFragment : Fragment(), OrderHistoryAdapter.OrderActionCallbacks {
|
|||||||
_binding = null
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
// private fun observeOrderCompletionStatus() {
|
override fun onResume() {
|
||||||
// viewModel.orderCompletionStatus.observe(viewLifecycleOwner) { result ->
|
super.onResume()
|
||||||
// when (result) {
|
observeOrderList()
|
||||||
// is Result.Loading -> {
|
}
|
||||||
// // Handle loading state if needed
|
|
||||||
// }
|
|
||||||
// is Result.Success -> {
|
|
||||||
// Toast.makeText(requireContext(), "Order completed successfully!", Toast.LENGTH_SHORT).show()
|
|
||||||
//// loadOrders()
|
|
||||||
// }
|
|
||||||
// is Result.Error -> {
|
|
||||||
// Toast.makeText(requireContext(), "Failed to complete order: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
@ -67,7 +67,7 @@ class CancelOrderBottomSheet(
|
|||||||
|
|
||||||
btnConfirm.setOnClickListener {
|
btnConfirm.setOnClickListener {
|
||||||
if (selectedReason == null) {
|
if (selectedReason == null) {
|
||||||
Toast.makeText(context, "Please select a reason", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "Pilih alasan pembatalan", Toast.LENGTH_SHORT).show()
|
||||||
return@setOnClickListener
|
return@setOnClickListener
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -90,7 +90,7 @@ class CreateReviewActivity : AppCompatActivity() {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Toast.makeText(this, "Error loading review items", Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, "Gagal memuat ulasan", Toast.LENGTH_SHORT).show()
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -110,7 +110,7 @@ class CreateReviewActivity : AppCompatActivity() {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(this, "No items to review", Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, "Tidak ada produk untuk direview", Toast.LENGTH_SHORT).show()
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -112,13 +112,17 @@ class DetailProductActivity : AppCompatActivity() {
|
|||||||
when (result) {
|
when (result) {
|
||||||
is Result.Success -> {
|
is Result.Success -> {
|
||||||
updateStoreInfo(result.data)
|
updateStoreInfo(result.data)
|
||||||
|
binding.progressBarDetailStore.visibility = View.GONE
|
||||||
}
|
}
|
||||||
is Result.Error -> {
|
is Result.Error -> {
|
||||||
// Show error message, maybe a Toast or Snackbar
|
// Show error message, maybe a Toast or Snackbar
|
||||||
Toast.makeText(this, "Failed to load store: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
binding.progressBarDetailStore.visibility = View.GONE
|
||||||
|
Log.e("DetailProfileActivity", "Failed to load store: ${result.exception.message}")
|
||||||
|
Toast.makeText(this, "Kendala memuat toko", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
is Result.Loading -> {
|
is Result.Loading -> {
|
||||||
// Show loading indicator if needed
|
// Show loading indicator if needed
|
||||||
|
binding.progressBarDetailStore.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -160,6 +164,10 @@ class DetailProductActivity : AppCompatActivity() {
|
|||||||
val products = viewModel.otherProducts.value.orEmpty()
|
val products = viewModel.otherProducts.value.orEmpty()
|
||||||
if (products.isNotEmpty()) {
|
if (products.isNotEmpty()) {
|
||||||
updateOtherProducts(products, storeMap)
|
updateOtherProducts(products, storeMap)
|
||||||
|
} else {
|
||||||
|
binding.emptyOtherProducts.visibility = View.VISIBLE
|
||||||
|
binding.recyclerViewOtherProducts.visibility = View.GONE
|
||||||
|
binding.tvViewAllProducts.visibility = View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -190,12 +198,14 @@ class DetailProductActivity : AppCompatActivity() {
|
|||||||
private fun updateOtherProducts(products: List<ProductsItem>, storeMap: Map<Int, StoreItem>) {
|
private fun updateOtherProducts(products: List<ProductsItem>, storeMap: Map<Int, StoreItem>) {
|
||||||
if (products.isEmpty()) {
|
if (products.isEmpty()) {
|
||||||
Log.d("DetailProductActivity", "Product list is empty, hiding RecyclerView")
|
Log.d("DetailProductActivity", "Product list is empty, hiding RecyclerView")
|
||||||
binding.recyclerViewOtherProducts.visibility = View.VISIBLE
|
binding.recyclerViewOtherProducts.visibility = View.GONE
|
||||||
|
binding.emptyOtherProducts.visibility = View.VISIBLE
|
||||||
binding.tvViewAllProducts.visibility = View.GONE
|
binding.tvViewAllProducts.visibility = View.GONE
|
||||||
} else {
|
} else {
|
||||||
Log.d("DetailProductActivity", "Displaying product list in RecyclerView")
|
Log.d("DetailProductActivity", "Displaying product list in RecyclerView")
|
||||||
binding.recyclerViewOtherProducts.visibility = View.VISIBLE
|
binding.recyclerViewOtherProducts.visibility = View.VISIBLE
|
||||||
binding.tvViewAllProducts.visibility = View.VISIBLE
|
binding.tvViewAllProducts.visibility = View.VISIBLE
|
||||||
|
binding.emptyOtherProducts.visibility = View.GONE
|
||||||
|
|
||||||
productAdapter = OtherProductAdapter(products, onClick = { product ->
|
productAdapter = OtherProductAdapter(products, onClick = { product ->
|
||||||
handleProductClick(product)
|
handleProductClick(product)
|
||||||
@ -316,11 +326,13 @@ class DetailProductActivity : AppCompatActivity() {
|
|||||||
val limitedReviewList = if (reviewList.isNotEmpty()) listOf(reviewList.first()) else emptyList()
|
val limitedReviewList = if (reviewList.isNotEmpty()) listOf(reviewList.first()) else emptyList()
|
||||||
if (reviewList.isEmpty()) {
|
if (reviewList.isEmpty()) {
|
||||||
binding.recyclerViewReviews.visibility = View.GONE
|
binding.recyclerViewReviews.visibility = View.GONE
|
||||||
|
binding.emptyReview.visibility = View.VISIBLE
|
||||||
binding.tvViewAllReviews.visibility = View.GONE
|
binding.tvViewAllReviews.visibility = View.GONE
|
||||||
// binding.tvNoReviews.visibility = View.VISIBLE
|
// binding.tvNoReviews.visibility = View.VISIBLE
|
||||||
} else {
|
} else {
|
||||||
binding.recyclerViewReviews.visibility = View.VISIBLE
|
binding.recyclerViewReviews.visibility = View.VISIBLE
|
||||||
binding.tvViewAllReviews.visibility = View.VISIBLE
|
binding.tvViewAllReviews.visibility = View.VISIBLE
|
||||||
|
binding.emptyReview.visibility = View.GONE
|
||||||
}
|
}
|
||||||
// binding.tvNoReviews.visibility = View.GONE
|
// binding.tvNoReviews.visibility = View.GONE
|
||||||
reviewsAdapter = ReviewsAdapter(
|
reviewsAdapter = ReviewsAdapter(
|
||||||
@ -519,7 +531,11 @@ class DetailProductActivity : AppCompatActivity() {
|
|||||||
attachProduct = true // This will auto-attach the product!
|
attachProduct = true // This will auto-attach the product!
|
||||||
|
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
loadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@ -64,10 +64,9 @@ class StoreDetailActivity : AppCompatActivity() {
|
|||||||
)
|
)
|
||||||
windowInsets
|
windowInsets
|
||||||
}
|
}
|
||||||
|
loadData()
|
||||||
setupUI()
|
setupUI()
|
||||||
setupObservers()
|
setupObservers()
|
||||||
loadData()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupUI() {
|
private fun setupUI() {
|
||||||
@ -88,15 +87,18 @@ class StoreDetailActivity : AppCompatActivity() {
|
|||||||
viewModel.storeDetail.observe(this) { result ->
|
viewModel.storeDetail.observe(this) { result ->
|
||||||
when (result) {
|
when (result) {
|
||||||
is Result.Success -> {
|
is Result.Success -> {
|
||||||
|
binding.progressBarDetailProdItem.visibility = View.GONE
|
||||||
updateStoreInfo(result.data)
|
updateStoreInfo(result.data)
|
||||||
viewModel.loadOtherProducts(result.data.storeId)
|
viewModel.loadOtherProducts(result.data.storeId)
|
||||||
}
|
}
|
||||||
is Result.Error -> {
|
is Result.Error -> {
|
||||||
// Show error message, maybe a Toast or Snackbar
|
// Show error message, maybe a Toast or Snackbar
|
||||||
|
binding.progressBarDetailProdItem.visibility = View.GONE
|
||||||
Toast.makeText(this, "Failed to load store: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, "Failed to load store: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
is Result.Loading -> {
|
is Result.Loading -> {
|
||||||
// Show loading indicator if needed
|
// Show loading indicator if needed
|
||||||
|
binding.progressBarDetailProdItem.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -109,6 +111,9 @@ class StoreDetailActivity : AppCompatActivity() {
|
|||||||
val products = viewModel.otherProducts.value.orEmpty()
|
val products = viewModel.otherProducts.value.orEmpty()
|
||||||
if (products.isNotEmpty()) {
|
if (products.isNotEmpty()) {
|
||||||
updateProducts(products, storeMap)
|
updateProducts(products, storeMap)
|
||||||
|
} else {
|
||||||
|
binding.progressBarDetailProdItem.visibility = View.VISIBLE
|
||||||
|
binding.rvProducts.visibility = View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -146,7 +151,7 @@ class StoreDetailActivity : AppCompatActivity() {
|
|||||||
.into(binding.ivStoreImage)
|
.into(binding.ivStoreImage)
|
||||||
|
|
||||||
val ratingStr = it.storeRating
|
val ratingStr = it.storeRating
|
||||||
val ratingValue = ratingStr?.toFloatOrNull()
|
val ratingValue = ratingStr?.toFloatOrNull() ?: 0f
|
||||||
|
|
||||||
if (ratingValue != null && ratingValue > 0f) {
|
if (ratingValue != null && ratingValue > 0f) {
|
||||||
binding.tvStoreRating.text = String.format("%.1f", ratingValue)
|
binding.tvStoreRating.text = String.format("%.1f", ratingValue)
|
||||||
@ -161,10 +166,12 @@ class StoreDetailActivity : AppCompatActivity() {
|
|||||||
private fun updateProducts(products: List<ProductsItem>, storeMap: Map<Int, StoreItem>) {
|
private fun updateProducts(products: List<ProductsItem>, storeMap: Map<Int, StoreItem>) {
|
||||||
if (products.isEmpty()) {
|
if (products.isEmpty()) {
|
||||||
binding.rvProducts.visibility = View.GONE
|
binding.rvProducts.visibility = View.GONE
|
||||||
|
binding.progressBarDetailProdItem.visibility = View.VISIBLE
|
||||||
Log.d("StoreDetailActivity", "Product list is empty, hiding RecyclerView")
|
Log.d("StoreDetailActivity", "Product list is empty, hiding RecyclerView")
|
||||||
} else {
|
} else {
|
||||||
Log.d("StoreDetailActivity", "Displaying product list in RecyclerView")
|
Log.d("StoreDetailActivity", "Displaying product list in RecyclerView")
|
||||||
|
|
||||||
|
binding.progressBarDetailProdItem.visibility = View.GONE
|
||||||
binding.rvProducts.visibility = View.VISIBLE
|
binding.rvProducts.visibility = View.VISIBLE
|
||||||
productAdapter = HorizontalProductAdapter(products, onClick = { product ->
|
productAdapter = HorizontalProductAdapter(products, onClick = { product ->
|
||||||
handleProductClick(product)
|
handleProductClick(product)
|
||||||
|
|||||||
@ -45,11 +45,11 @@ class StoreDetailViewModel (private val repository: ProductRepository
|
|||||||
} // Filter by storeId and exclude current product
|
} // Filter by storeId and exclude current product
|
||||||
_otherProducts.value = filteredProducts // Update LiveData
|
_otherProducts.value = filteredProducts // Update LiveData
|
||||||
} else if (result is Result.Error) {
|
} else if (result is Result.Error) {
|
||||||
Log.e("ProductViewModel", "Error loading other products: ${result.exception.message}")
|
Log.e("StoreDetailViewModel", "Error loading other products: ${result.exception.message}")
|
||||||
_otherProducts.value = emptyList() // Set empty list on failure
|
_otherProducts.value = emptyList() // Set empty list on failure
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("ProductViewModel", "Exception loading other products: ${e.message}")
|
Log.e("StoreDetailViewModel", "Exception loading other products: ${e.message}")
|
||||||
_otherProducts.value = emptyList()
|
_otherProducts.value = emptyList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,7 +67,7 @@ class StoreDetailViewModel (private val repository: ProductRepository
|
|||||||
loadStoreDetail(storeId)
|
loadStoreDetail(storeId)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("ProductViewModel", "Error loading product details: ${e.message}")
|
Log.e("StoreDetailViewModel", "Error loading product details: ${e.message}")
|
||||||
_error.value = "Failed to load product details: ${e.message}"
|
_error.value = "Failed to load product details: ${e.message}"
|
||||||
} finally {
|
} finally {
|
||||||
_isLoading.value = false
|
_isLoading.value = false
|
||||||
@ -82,7 +82,7 @@ class StoreDetailViewModel (private val repository: ProductRepository
|
|||||||
val result = repository.fetchStoreDetail(storeId)
|
val result = repository.fetchStoreDetail(storeId)
|
||||||
_storeDetail.value = result
|
_storeDetail.value = result
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("ProductViewModel", "Error loading store details: ${e.message}")
|
Log.e("StoreDetailViewModel", "Error loading store details: ${e.message}")
|
||||||
_storeDetail.value = Result.Error(e)
|
_storeDetail.value = Result.Error(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,10 +99,10 @@ class StoreDetailViewModel (private val repository: ProductRepository
|
|||||||
if (result is Result.Success) {
|
if (result is Result.Success) {
|
||||||
map[storeId] = result.data
|
map[storeId] = result.data
|
||||||
} else if (result is Result.Error) {
|
} else if (result is Result.Error) {
|
||||||
Log.e("ProductViewModel", "Failed to load storeId $storeId", result.exception)
|
Log.e("StoreDetailViewModel", "Failed to load storeId $storeId", result.exception)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("ProductViewModel", "Exception fetching storeId $storeId", e)
|
Log.e("StoreDetailViewModel", "Exception fetching storeId $storeId", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,73 @@
|
|||||||
|
package com.alya.ecommerce_serang.ui.profile
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.activity.enableEdgeToEdge
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.view.ViewCompat
|
||||||
|
import androidx.core.view.WindowInsetsCompat
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import com.alya.ecommerce_serang.R
|
||||||
|
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||||
|
import com.alya.ecommerce_serang.data.api.retrofit.ApiService
|
||||||
|
import com.alya.ecommerce_serang.data.repository.Result
|
||||||
|
import com.alya.ecommerce_serang.data.repository.UserRepository
|
||||||
|
import com.alya.ecommerce_serang.databinding.ActivityChangePasswordBinding
|
||||||
|
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||||
|
import com.alya.ecommerce_serang.utils.SessionManager
|
||||||
|
import com.alya.ecommerce_serang.utils.viewmodel.ProfileViewModel
|
||||||
|
import kotlin.getValue
|
||||||
|
|
||||||
|
class ChangePasswordActivity : AppCompatActivity() {
|
||||||
|
private lateinit var binding: ActivityChangePasswordBinding
|
||||||
|
private lateinit var apiService: ApiService
|
||||||
|
private lateinit var sessionManager: SessionManager
|
||||||
|
|
||||||
|
private val viewModel: ProfileViewModel by viewModels {
|
||||||
|
BaseViewModelFactory {
|
||||||
|
apiService = ApiConfig.getApiService(sessionManager)
|
||||||
|
val userRepository = UserRepository(apiService)
|
||||||
|
ProfileViewModel(userRepository)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
binding = ActivityChangePasswordBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
sessionManager = SessionManager(this)
|
||||||
|
|
||||||
|
// Listen for the result of the password change
|
||||||
|
viewModel.changePasswordResult.observe(this, Observer { result ->
|
||||||
|
when (result) {
|
||||||
|
is Result.Error -> {
|
||||||
|
Toast.makeText(
|
||||||
|
this,
|
||||||
|
"Gagal mengubah kata sandi: ${result.exception.message}",
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
is Result.Success -> {
|
||||||
|
Toast.makeText(this, "Berhasil mengubah kata sandi", Toast.LENGTH_SHORT).show()
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
is Result.Loading -> {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Button to trigger password change
|
||||||
|
binding.btnChangePass.setOnClickListener {
|
||||||
|
val currentPassword = binding.etLoginPassword.text.toString()
|
||||||
|
val newPassword = binding.etLoginNewPassword.text.toString()
|
||||||
|
|
||||||
|
if (currentPassword.isNotEmpty() && newPassword.isNotEmpty()) {
|
||||||
|
// Call change password function from ViewModel
|
||||||
|
viewModel.changePassword(currentPassword, newPassword)
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, "Lengkapi data", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -90,6 +90,8 @@ class DetailProfileActivity : AppCompatActivity() {
|
|||||||
Log.e("DetailProfileActivity", "Error from ViewModel: $error")
|
Log.e("DetailProfileActivity", "Error from ViewModel: $error")
|
||||||
Toast.makeText(this, error, Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, error, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupClickListeners() {
|
private fun setupClickListeners() {
|
||||||
@ -106,7 +108,8 @@ class DetailProfileActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
editProfileLauncher.launch(intent)
|
editProfileLauncher.launch(intent)
|
||||||
} ?: run {
|
} ?: run {
|
||||||
Toast.makeText(this, "Profile data is not available", Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, "Akun tidak ditemukan", Toast.LENGTH_SHORT).show()
|
||||||
|
Log.e("DetailProfileActivity", "Profile data is not available")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,10 +20,10 @@ import com.alya.ecommerce_serang.data.repository.MyStoreRepository
|
|||||||
import com.alya.ecommerce_serang.data.repository.UserRepository
|
import com.alya.ecommerce_serang.data.repository.UserRepository
|
||||||
import com.alya.ecommerce_serang.databinding.FragmentProfileBinding
|
import com.alya.ecommerce_serang.databinding.FragmentProfileBinding
|
||||||
import com.alya.ecommerce_serang.ui.auth.LoginActivity
|
import com.alya.ecommerce_serang.ui.auth.LoginActivity
|
||||||
import com.alya.ecommerce_serang.ui.profile.mystore.RegisterStoreActivity
|
|
||||||
import com.alya.ecommerce_serang.ui.order.address.AddressActivity
|
import com.alya.ecommerce_serang.ui.order.address.AddressActivity
|
||||||
import com.alya.ecommerce_serang.ui.order.history.HistoryActivity
|
import com.alya.ecommerce_serang.ui.order.history.HistoryActivity
|
||||||
import com.alya.ecommerce_serang.ui.profile.mystore.MyStoreActivity
|
import com.alya.ecommerce_serang.ui.profile.mystore.MyStoreActivity
|
||||||
|
import com.alya.ecommerce_serang.ui.profile.mystore.RegisterStoreActivity
|
||||||
import com.alya.ecommerce_serang.ui.profile.mystore.StoreOnReviewActivity
|
import com.alya.ecommerce_serang.ui.profile.mystore.StoreOnReviewActivity
|
||||||
import com.alya.ecommerce_serang.ui.profile.mystore.StoreSuspendedActivity
|
import com.alya.ecommerce_serang.ui.profile.mystore.StoreSuspendedActivity
|
||||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||||
@ -58,7 +58,6 @@ class ProfileFragment : Fragment() {
|
|||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
sessionManager = SessionManager(requireContext())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
@ -72,26 +71,57 @@ class ProfileFragment : Fragment() {
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
sessionManager = SessionManager(requireContext())
|
||||||
|
|
||||||
|
if (!sessionManager.isLoggedIn()) {
|
||||||
|
// Redirect to LoginActivity
|
||||||
|
binding.tvName.text = "Selamat Datang"
|
||||||
|
binding.tvUsername.text = "Silahkan masuk"
|
||||||
|
binding.btnDetailProfile.text = "Masuk"
|
||||||
|
binding.btnDetailProfile.setOnClickListener {
|
||||||
|
val intent = Intent(requireContext(), LoginActivity::class.java)
|
||||||
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||||
|
startActivity(intent)
|
||||||
|
|
||||||
|
// ✅ Finish the host activity so user can’t go back
|
||||||
|
requireActivity().finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.containerBukaToko.visibility = View.GONE
|
||||||
|
binding.cardPesanan.visibility = View.GONE
|
||||||
|
binding.tvPengaturanAkun.visibility = View.GONE
|
||||||
|
binding.containerSettings.visibility = View.GONE
|
||||||
|
binding.cardAbout.visibility = View.GONE
|
||||||
|
binding.cardLogout.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
viewModel.loadUserProfile()
|
||||||
|
viewModel.checkStoreUser()
|
||||||
|
|
||||||
observeUserProfile()
|
observeUserProfile()
|
||||||
|
|
||||||
observeStoreStatus()
|
observeStoreStatus()
|
||||||
|
|
||||||
viewModel.loadUserProfile()
|
|
||||||
viewModel.checkStoreUser()
|
|
||||||
|
|
||||||
binding.cardBukaToko.setOnClickListener{
|
binding.cardBukaToko.setOnClickListener{
|
||||||
// if (hasStore == true) startActivity(Intent(requireContext(), MyStoreActivity::class.java))
|
// if (hasStore == true) startActivity(Intent(requireContext(), MyStoreActivity::class.java))
|
||||||
// else startActivity(Intent(requireContext(), RegisterStoreActivity::class.java))
|
// else startActivity(Intent(requireContext(), RegisterStoreActivity::class.java))
|
||||||
if (viewModel.checkStore.value == true) {
|
if (viewModel.checkStore.value == true) {
|
||||||
myStoreViewModel.loadMyStore()
|
myStoreViewModel.loadMyStore()
|
||||||
myStoreViewModel.myStoreProfile.observe(viewLifecycleOwner) { store ->
|
myStoreViewModel.myStoreProfile.observe(viewLifecycleOwner) { storeDataResponse ->
|
||||||
store?.let {
|
storeDataResponse?.let { storeResponse ->
|
||||||
when (store.storeStatus) {
|
val store = storeResponse.store
|
||||||
|
when (store.approvalStatus) {
|
||||||
"process" -> startActivity(Intent(requireContext(), StoreOnReviewActivity::class.java))
|
"process" -> startActivity(Intent(requireContext(), StoreOnReviewActivity::class.java))
|
||||||
"active" -> startActivity(Intent(requireContext(), MyStoreActivity::class.java))
|
"rejected" -> startActivity(
|
||||||
"inactive" -> startActivity(Intent(requireContext(), MyStoreActivity::class.java))
|
Intent(requireContext(), RegisterStoreActivity::class.java).putExtra("REAPPLY", true))
|
||||||
"suspended" -> startActivity(Intent(requireContext(), StoreSuspendedActivity::class.java))
|
else -> {
|
||||||
else -> startActivity(Intent(requireContext(), RegisterStoreActivity::class.java))
|
when(store.storeStatus){
|
||||||
|
"suspended" -> startActivity(Intent(requireContext(), StoreSuspendedActivity::class.java))
|
||||||
|
else -> startActivity(Intent(requireContext(), MyStoreActivity::class.java))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} ?: run {
|
} ?: run {
|
||||||
Toast.makeText(requireContext(), "Gagal memuat data toko", Toast.LENGTH_SHORT).show()
|
Toast.makeText(requireContext(), "Gagal memuat data toko", Toast.LENGTH_SHORT).show()
|
||||||
@ -115,6 +145,11 @@ class ProfileFragment : Fragment() {
|
|||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binding.cardChangePass.setOnClickListener{
|
||||||
|
val intent = Intent(requireContext(), ChangePasswordActivity::class.java)
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
binding.cardLogout.setOnClickListener{
|
binding.cardLogout.setOnClickListener{
|
||||||
logout()
|
logout()
|
||||||
}
|
}
|
||||||
@ -130,7 +165,8 @@ class ProfileFragment : Fragment() {
|
|||||||
user?.let { updateUI(it) }
|
user?.let { updateUI(it) }
|
||||||
}
|
}
|
||||||
viewModel.errorMessage.observe(viewLifecycleOwner) { errorMessage ->
|
viewModel.errorMessage.observe(viewLifecycleOwner) { errorMessage ->
|
||||||
Toast.makeText(requireContext(), errorMessage, Toast.LENGTH_SHORT).show()
|
// Toast.makeText(requireContext(), errorMessage, Toast.LENGTH_SHORT).show()
|
||||||
|
Log.e("Profile Fragment", "Failed to load profile: $errorMessage")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,6 +222,8 @@ class ProfileFragment : Fragment() {
|
|||||||
sessionManager.clearAll()
|
sessionManager.clearAll()
|
||||||
val intent = Intent(requireContext(), LoginActivity::class.java)
|
val intent = Intent(requireContext(), LoginActivity::class.java)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
|
requireActivity().finish()
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
requireContext(),
|
requireContext(),
|
||||||
@ -196,4 +234,11 @@ class ProfileFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
viewModel.loadUserProfile()
|
||||||
|
viewModel.checkStoreUser()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -2,6 +2,7 @@ package com.alya.ecommerce_serang.ui.profile.editprofile
|
|||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.app.AlertDialog
|
||||||
import android.app.DatePickerDialog
|
import android.app.DatePickerDialog
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
@ -24,7 +25,6 @@ import com.alya.ecommerce_serang.BuildConfig.BASE_URL
|
|||||||
import com.alya.ecommerce_serang.R
|
import com.alya.ecommerce_serang.R
|
||||||
import com.alya.ecommerce_serang.data.api.dto.UserProfile
|
import com.alya.ecommerce_serang.data.api.dto.UserProfile
|
||||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiService
|
|
||||||
import com.alya.ecommerce_serang.data.repository.Result
|
import com.alya.ecommerce_serang.data.repository.Result
|
||||||
import com.alya.ecommerce_serang.data.repository.UserRepository
|
import com.alya.ecommerce_serang.data.repository.UserRepository
|
||||||
import com.alya.ecommerce_serang.databinding.ActivityEditProfileCustBinding
|
import com.alya.ecommerce_serang.databinding.ActivityEditProfileCustBinding
|
||||||
@ -33,7 +33,6 @@ import com.alya.ecommerce_serang.utils.SessionManager
|
|||||||
import com.alya.ecommerce_serang.utils.viewmodel.ProfileViewModel
|
import com.alya.ecommerce_serang.utils.viewmodel.ProfileViewModel
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import java.io.File
|
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
@ -41,9 +40,9 @@ import java.util.TimeZone
|
|||||||
|
|
||||||
class EditProfileCustActivity : AppCompatActivity() {
|
class EditProfileCustActivity : AppCompatActivity() {
|
||||||
private lateinit var binding: ActivityEditProfileCustBinding
|
private lateinit var binding: ActivityEditProfileCustBinding
|
||||||
private lateinit var apiService: ApiService
|
|
||||||
private lateinit var sessionManager: SessionManager
|
private lateinit var sessionManager: SessionManager
|
||||||
private var selectedImageUri: Uri? = null
|
private var selectedImageUri: Uri? = null
|
||||||
|
private var currentUser: UserProfile? = null
|
||||||
|
|
||||||
private val viewModel: ProfileViewModel by viewModels {
|
private val viewModel: ProfileViewModel by viewModels {
|
||||||
BaseViewModelFactory {
|
BaseViewModelFactory {
|
||||||
@ -54,7 +53,7 @@ class EditProfileCustActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val getContent = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
private val getContent = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||||
if (result.resultCode == Activity.RESULT_OK) {
|
if (result.resultCode == RESULT_OK) {
|
||||||
val data: Intent? = result.data
|
val data: Intent? = result.data
|
||||||
data?.data?.let {
|
data?.data?.let {
|
||||||
selectedImageUri = it
|
selectedImageUri = it
|
||||||
@ -105,8 +104,8 @@ class EditProfileCustActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
userProfile?.let {
|
userProfile?.let {
|
||||||
|
currentUser = it
|
||||||
populateFields(it)
|
populateFields(it)
|
||||||
|
|
||||||
setupClickListeners()
|
setupClickListeners()
|
||||||
observeViewModel()
|
observeViewModel()
|
||||||
}
|
}
|
||||||
@ -118,7 +117,7 @@ class EditProfileCustActivity : AppCompatActivity() {
|
|||||||
binding.etNumberPhoneUser.setText(profile.phone)
|
binding.etNumberPhoneUser.setText(profile.phone)
|
||||||
|
|
||||||
// Format birth date for display
|
// Format birth date for display
|
||||||
profile.birthDate?.let {
|
profile.birthDate.let {
|
||||||
binding.etDateBirth.setText(formatDate(it))
|
binding.etDateBirth.setText(formatDate(it))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +155,7 @@ class EditProfileCustActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
binding.btnSave.setOnClickListener {
|
binding.btnSave.setOnClickListener {
|
||||||
saveProfile()
|
if (hasChanged()) confirmUpdate() else finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,25 +212,38 @@ class EditProfileCustActivity : AppCompatActivity() {
|
|||||||
datePickerDialog.show()
|
datePickerDialog.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun confirmUpdate() {
|
||||||
|
AlertDialog.Builder(this)
|
||||||
|
.setTitle("Konfirmasi Perubahan")
|
||||||
|
.setMessage("Apakah Anda yakin ingin menyimpan perubahan profil toko Anda?")
|
||||||
|
.setPositiveButton("Ya") { _, _ -> saveProfile() }
|
||||||
|
.setNegativeButton("Batal", null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hasChanged(): Boolean {
|
||||||
|
val name = binding.etNameUser.text.toString() != currentUser?.name
|
||||||
|
val username = binding.etUsername.text.toString() != currentUser?.username
|
||||||
|
val email = binding.etEmailUser.text.toString() != currentUser?.email
|
||||||
|
val phone = binding.etNumberPhoneUser.text.toString() != currentUser?.phone
|
||||||
|
val displayDate = binding.etDateBirth.text.toString() != currentUser?.birthDate.toString()
|
||||||
|
val imgProfile = selectedImageUri != null
|
||||||
|
return name || username || email || phone || displayDate || imgProfile
|
||||||
|
}
|
||||||
|
|
||||||
private fun saveProfile() {
|
private fun saveProfile() {
|
||||||
val name = binding.etNameUser.text.toString()
|
val name = binding.etNameUser.text.toString()
|
||||||
val username = binding.etUsername.text.toString()
|
val username = binding.etUsername.text.toString()
|
||||||
val email = binding.etEmailUser.text.toString()
|
val email = binding.etEmailUser.text.toString()
|
||||||
val phone = binding.etNumberPhoneUser.text.toString()
|
val phone = binding.etNumberPhoneUser.text.toString()
|
||||||
val displayDate = binding.etDateBirth.text.toString()
|
val displayDate = binding.etDateBirth.text.toString()
|
||||||
|
val imgProfile = selectedImageUri
|
||||||
|
|
||||||
if (name.isEmpty() || username.isEmpty() || email.isEmpty() || phone.isEmpty() || displayDate.isEmpty()) {
|
|
||||||
Toast.makeText(this, "Semua field harus diisi", Toast.LENGTH_SHORT).show()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert date to server format
|
|
||||||
val serverBirthDate = convertToServerDateFormat(displayDate)
|
val serverBirthDate = convertToServerDateFormat(displayDate)
|
||||||
|
|
||||||
Log.d(TAG, "Starting profile save with direct method")
|
Log.d(TAG, "Starting profile save with direct method")
|
||||||
Log.d(TAG, "Selected image URI: $selectedImageUri")
|
Log.d(TAG, "Selected image URI: $selectedImageUri")
|
||||||
|
|
||||||
// Disable the button to prevent multiple clicks
|
|
||||||
binding.btnSave.isEnabled = false
|
binding.btnSave.isEnabled = false
|
||||||
|
|
||||||
// Call the repository method via ViewModel
|
// Call the repository method via ViewModel
|
||||||
@ -242,82 +254,10 @@ class EditProfileCustActivity : AppCompatActivity() {
|
|||||||
phone = phone,
|
phone = phone,
|
||||||
birthDate = serverBirthDate,
|
birthDate = serverBirthDate,
|
||||||
email = email,
|
email = email,
|
||||||
imageUri = selectedImageUri
|
imageUri = imgProfile
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getRealPathFromURI(uri: Uri): String? {
|
|
||||||
Log.d(TAG, "Getting real path from URI: $uri")
|
|
||||||
|
|
||||||
// Handle different URI schemes
|
|
||||||
when {
|
|
||||||
// File URI
|
|
||||||
uri.scheme == "file" -> {
|
|
||||||
val path = uri.path
|
|
||||||
Log.d(TAG, "URI is file scheme, path: $path")
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
|
|
||||||
// Content URI
|
|
||||||
uri.scheme == "content" -> {
|
|
||||||
try {
|
|
||||||
val projection = arrayOf(MediaStore.Images.Media.DATA)
|
|
||||||
contentResolver.query(uri, projection, null, null, null)?.use { cursor ->
|
|
||||||
if (cursor.moveToFirst()) {
|
|
||||||
val columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
|
|
||||||
val path = cursor.getString(columnIndex)
|
|
||||||
Log.d(TAG, "Found path from content URI: $path")
|
|
||||||
return path
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, "Cursor is empty")
|
|
||||||
}
|
|
||||||
} ?: Log.e(TAG, "Cursor is null")
|
|
||||||
|
|
||||||
// If the above fails, try the documented API way
|
|
||||||
contentResolver.openInputStream(uri)?.use { inputStream ->
|
|
||||||
// Create a temp file
|
|
||||||
val fileName = getFileName(uri) ?: "temp_img_${System.currentTimeMillis()}.jpg"
|
|
||||||
val tempFile = File(cacheDir, fileName)
|
|
||||||
tempFile.outputStream().use { outputStream ->
|
|
||||||
inputStream.copyTo(outputStream)
|
|
||||||
}
|
|
||||||
Log.d(TAG, "Created temporary file: ${tempFile.absolutePath}")
|
|
||||||
return tempFile.absolutePath
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(TAG, "Error getting real path: ${e.message}", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.e(TAG, "Could not get real path for URI: $uri")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getFileName(uri: Uri): String? {
|
|
||||||
var result: String? = null
|
|
||||||
if (uri.scheme == "content") {
|
|
||||||
contentResolver.query(uri, null, null, null, null)?.use { cursor ->
|
|
||||||
if (cursor.moveToFirst()) {
|
|
||||||
val columnIndex = cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME)
|
|
||||||
if (columnIndex >= 0) {
|
|
||||||
result = cursor.getString(columnIndex)
|
|
||||||
Log.d(TAG, "Found filename from content URI: $result")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (result == null) {
|
|
||||||
result = uri.path
|
|
||||||
val cut = result?.lastIndexOf('/') ?: -1
|
|
||||||
if (cut != -1) {
|
|
||||||
result = result?.substring(cut + 1)
|
|
||||||
}
|
|
||||||
Log.d(TAG, "Extracted filename from path: $result")
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun formatDate(dateString: String?): String {
|
private fun formatDate(dateString: String?): String {
|
||||||
if (dateString.isNullOrEmpty()) return "N/A"
|
if (dateString.isNullOrEmpty()) return "N/A"
|
||||||
|
|
||||||
|
|||||||
@ -59,12 +59,13 @@ class MyStoreActivity : AppCompatActivity() {
|
|||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
viewModel.myStoreProfile.observe(this){ user ->
|
||||||
|
user?.let { myStoreProfileOverview(it.store) }
|
||||||
|
}
|
||||||
|
|
||||||
viewModel.loadMyStore()
|
viewModel.loadMyStore()
|
||||||
viewModel.loadMyStoreProducts()
|
viewModel.loadMyStoreProducts()
|
||||||
|
viewModel.fetchBalance()
|
||||||
viewModel.myStoreProfile.observe(this){ user ->
|
|
||||||
user?.let { myStoreProfileOverview(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
viewModel.errorMessage.observe(this) { error ->
|
viewModel.errorMessage.observe(this) { error ->
|
||||||
Toast.makeText(this, error, Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, error, Toast.LENGTH_SHORT).show()
|
||||||
@ -72,7 +73,6 @@ class MyStoreActivity : AppCompatActivity() {
|
|||||||
setUpClickListeners()
|
setUpClickListeners()
|
||||||
getCountOrder()
|
getCountOrder()
|
||||||
observeViewModel()
|
observeViewModel()
|
||||||
viewModel.fetchBalance()
|
|
||||||
fetchBalance()
|
fetchBalance()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,6 +206,16 @@ class MyStoreActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
lifecycleScope.launch {
|
||||||
|
viewModel.getAllStatusCounts()
|
||||||
|
}
|
||||||
|
viewModel.loadMyStore()
|
||||||
|
viewModel.loadMyStoreProducts()
|
||||||
|
viewModel.fetchBalance()
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val PROFILE_REQUEST_CODE = 100
|
private const val PROFILE_REQUEST_CODE = 100
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,7 @@ import androidx.core.view.WindowInsetsCompat
|
|||||||
import com.alya.ecommerce_serang.R
|
import com.alya.ecommerce_serang.R
|
||||||
import com.alya.ecommerce_serang.data.api.response.auth.StoreTypesItem
|
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.api.retrofit.ApiConfig
|
||||||
|
import com.alya.ecommerce_serang.data.repository.MyStoreRepository
|
||||||
import com.alya.ecommerce_serang.data.repository.Result
|
import com.alya.ecommerce_serang.data.repository.Result
|
||||||
import com.alya.ecommerce_serang.data.repository.UserRepository
|
import com.alya.ecommerce_serang.data.repository.UserRepository
|
||||||
import com.alya.ecommerce_serang.databinding.ActivityRegisterStoreBinding
|
import com.alya.ecommerce_serang.databinding.ActivityRegisterStoreBinding
|
||||||
@ -35,8 +36,17 @@ import com.alya.ecommerce_serang.ui.order.address.CityAdapter
|
|||||||
import com.alya.ecommerce_serang.ui.order.address.ProvinceAdapter
|
import com.alya.ecommerce_serang.ui.order.address.ProvinceAdapter
|
||||||
import com.alya.ecommerce_serang.ui.order.address.SubdsitrictAdapter
|
import com.alya.ecommerce_serang.ui.order.address.SubdsitrictAdapter
|
||||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||||
|
import com.alya.ecommerce_serang.utils.FileUtils
|
||||||
|
import com.alya.ecommerce_serang.utils.ImageUtils
|
||||||
import com.alya.ecommerce_serang.utils.SessionManager
|
import com.alya.ecommerce_serang.utils.SessionManager
|
||||||
|
import com.alya.ecommerce_serang.utils.viewmodel.MyStoreViewModel
|
||||||
import com.alya.ecommerce_serang.utils.viewmodel.RegisterStoreViewModel
|
import com.alya.ecommerce_serang.utils.viewmodel.RegisterStoreViewModel
|
||||||
|
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||||
|
import okhttp3.MultipartBody
|
||||||
|
import okhttp3.RequestBody
|
||||||
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
|
import java.io.File
|
||||||
|
import androidx.core.net.toUri
|
||||||
|
|
||||||
class RegisterStoreActivity : AppCompatActivity() {
|
class RegisterStoreActivity : AppCompatActivity() {
|
||||||
|
|
||||||
@ -53,6 +63,7 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
private val PICK_KTP_REQUEST = 1002
|
private val PICK_KTP_REQUEST = 1002
|
||||||
private val PICK_NPWP_REQUEST = 1003
|
private val PICK_NPWP_REQUEST = 1003
|
||||||
private val PICK_NIB_REQUEST = 1004
|
private val PICK_NIB_REQUEST = 1004
|
||||||
|
private var isReapply: Boolean = false
|
||||||
|
|
||||||
// Location request code
|
// Location request code
|
||||||
private val LOCATION_PERMISSION_REQUEST = 2001
|
private val LOCATION_PERMISSION_REQUEST = 2001
|
||||||
@ -64,6 +75,15 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
RegisterStoreViewModel(orderRepository)
|
RegisterStoreViewModel(orderRepository)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val myStoreViewModel: MyStoreViewModel by viewModels {
|
||||||
|
BaseViewModelFactory {
|
||||||
|
val apiService = ApiConfig.getApiService(sessionManager)
|
||||||
|
val myStoreRepository = MyStoreRepository(apiService)
|
||||||
|
MyStoreViewModel(myStoreRepository)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityRegisterStoreBinding.inflate(layoutInflater)
|
binding = ActivityRegisterStoreBinding.inflate(layoutInflater)
|
||||||
@ -89,6 +109,8 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
setupHeader()
|
setupHeader()
|
||||||
|
|
||||||
|
isReapply = intent.getBooleanExtra("REAPPLY", false)
|
||||||
|
|
||||||
provinceAdapter = ProvinceAdapter(this)
|
provinceAdapter = ProvinceAdapter(this)
|
||||||
cityAdapter = CityAdapter(this)
|
cityAdapter = CityAdapter(this)
|
||||||
subdistrictAdapter = SubdsitrictAdapter(this)
|
subdistrictAdapter = SubdsitrictAdapter(this)
|
||||||
@ -129,19 +151,140 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
viewModel.cityId.observe(this) { validateRequiredFields() }
|
viewModel.cityId.observe(this) { validateRequiredFields() }
|
||||||
viewModel.storeTypeId.observe(this) { validateRequiredFields() }
|
viewModel.storeTypeId.observe(this) { validateRequiredFields() }
|
||||||
|
|
||||||
// Setup register button
|
if (isReapply) {
|
||||||
binding.btnRegister.setOnClickListener {
|
binding.btnRegister.text = "Ajukan Kembali"
|
||||||
Log.d(TAG, "Register button clicked")
|
binding.layoutRejected.visibility = View.VISIBLE
|
||||||
if (viewModel.validateForm()) {
|
|
||||||
Log.d(TAG, "Form validation successful, proceeding with registration")
|
myStoreViewModel.loadMyStore()
|
||||||
viewModel.registerStore(this)
|
|
||||||
} else {
|
myStoreViewModel.myStoreProfile.observe(this) { storeDataResponse ->
|
||||||
Log.e(TAG, "Form validation failed")
|
storeDataResponse?.let { storeResponse ->
|
||||||
Toast.makeText(this, "Harap lengkapi semua field yang wajib diisi", Toast.LENGTH_SHORT).show()
|
val store = storeResponse.store
|
||||||
|
binding.tvRejectedReason.text = store.approvalReason
|
||||||
|
|
||||||
|
// Prefill basic fields
|
||||||
|
binding.etStoreName.setText(store.storeName)
|
||||||
|
binding.etStoreDescription.setText(store.storeDescription)
|
||||||
|
binding.etStreet.setText(store.street)
|
||||||
|
binding.etPostalCode.setText(store.postalCode)
|
||||||
|
binding.etAddressDetail.setText(store.detail)
|
||||||
|
|
||||||
|
viewModel.storeName.value = store.storeName
|
||||||
|
viewModel.storeDescription.value = store.storeDescription
|
||||||
|
viewModel.street.value = store.street
|
||||||
|
viewModel.postalCode.value = store.postalCode.toIntOrNull() ?: 0
|
||||||
|
viewModel.addressDetail.value = store.detail
|
||||||
|
|
||||||
|
// Prefill bank info
|
||||||
|
storeResponse.payment.firstOrNull()?.let { payment ->
|
||||||
|
viewModel.bankName.value = payment.bankName
|
||||||
|
viewModel.bankNumber.value = payment.bankNum.toIntOrNull() ?: 0
|
||||||
|
val bankPosition = bankAdapter.findPositionByName(payment.bankName)
|
||||||
|
binding.spinnerBankName.setSelection(bankPosition, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefill couriers
|
||||||
|
storeResponse.shipping.forEach { courier ->
|
||||||
|
when (courier.courier) {
|
||||||
|
"jne" -> binding.checkboxJne.isChecked = true
|
||||||
|
"pos" -> binding.checkboxPos.isChecked = true
|
||||||
|
"tiki" -> binding.checkboxTiki.isChecked = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefill document URIs
|
||||||
|
store.ktp.let { ktpUri ->
|
||||||
|
viewModel.ktpUri = ktpUri.toUri()
|
||||||
|
updateImagePreview(viewModel.ktpUri, binding.imgKtp, binding.layoutUploadKtp)
|
||||||
|
}
|
||||||
|
store.npwp.let { npwpUri ->
|
||||||
|
viewModel.npwpUri = npwpUri.toUri()
|
||||||
|
updateDocumentPreview(binding.layoutUploadNpwp)
|
||||||
|
}
|
||||||
|
store.nib.let { nibUri ->
|
||||||
|
viewModel.nibUri = nibUri.toUri()
|
||||||
|
updateDocumentPreview(binding.layoutUploadNib)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefill spinner for store types
|
||||||
|
preselectStoreType(store.storeTypeId)
|
||||||
|
|
||||||
|
// Prefill province, city, and subdistrict
|
||||||
|
preselectProvinceCitySubdistrict(
|
||||||
|
provinceId = store.provinceId,
|
||||||
|
cityId = store.cityId,
|
||||||
|
subdistrictId = store.subdistrict
|
||||||
|
)
|
||||||
|
|
||||||
|
validateRequiredFields()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.btnRegister.setOnClickListener {
|
||||||
|
doUpdateStoreProfile()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
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 preselectStoreType(storeTypeId: Int) {
|
||||||
|
// The adapter is created in setupStoreTypeSpinner(...)
|
||||||
|
val adapter = binding.spinnerStoreType.adapter
|
||||||
|
if (adapter != null) {
|
||||||
|
val count = adapter.count
|
||||||
|
for (i in 0 until count) {
|
||||||
|
val item = adapter.getItem(i) as? StoreTypesItem
|
||||||
|
if (item?.id == storeTypeId) {
|
||||||
|
binding.spinnerStoreType.setSelection(i, false)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun preselectProvinceCitySubdistrict(
|
||||||
|
provinceId: Int,
|
||||||
|
cityId: String,
|
||||||
|
subdistrictId: String
|
||||||
|
) {
|
||||||
|
// Province first (this will trigger cities fetch)
|
||||||
|
val provCount = provinceAdapter.count
|
||||||
|
for (i in 0 until provCount) {
|
||||||
|
if (provinceAdapter.getProvinceId(i) == provinceId) {
|
||||||
|
binding.spinnerProvince.setSelection(i, false)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d(TAG, "onCreate: RegisterStoreActivity setup completed")
|
// When cities arrive, select the city, then load subdistricts
|
||||||
|
viewModel.citiesState.observe(this) { state ->
|
||||||
|
if (state is Result.Success) {
|
||||||
|
val cityCount = cityAdapter.count
|
||||||
|
for (i in 0 until cityCount) {
|
||||||
|
if (cityAdapter.getCityId(i) == cityId) {
|
||||||
|
binding.spinnerCity.setSelection(i, false)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// When subdistricts arrive, select the subdistrict
|
||||||
|
viewModel.subdistrictState.observe(this) { state ->
|
||||||
|
if (state is Result.Success) {
|
||||||
|
val subCount = subdistrictAdapter.count
|
||||||
|
for (i in 0 until subCount) {
|
||||||
|
if (subdistrictAdapter.getSubdistrictId(i) == subdistrictId) {
|
||||||
|
binding.spinnerSubdistrict.setSelection(i, false)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupHeader() {
|
private fun setupHeader() {
|
||||||
@ -177,10 +320,11 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
if (isFormValid) {
|
if (isFormValid) {
|
||||||
binding.btnRegister.setBackgroundResource(R.drawable.bg_button_active)
|
binding.btnRegister.setBackgroundResource(R.drawable.bg_button_active)
|
||||||
binding.btnRegister.setTextColor(ContextCompat.getColor(this, R.color.white))
|
binding.btnRegister.setTextColor(ContextCompat.getColor(this, R.color.white))
|
||||||
|
binding.btnRegister.isEnabled = true
|
||||||
} else {
|
} else {
|
||||||
binding.btnRegister.setBackgroundResource(R.drawable.bg_button_disabled)
|
binding.btnRegister.setBackgroundResource(R.drawable.bg_button_disabled)
|
||||||
binding.btnRegister.setTextColor(ContextCompat.getColor(this, R.color.black_300))
|
binding.btnRegister.setTextColor(ContextCompat.getColor(this, R.color.black_300))
|
||||||
|
binding.btnRegister.isEnabled = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,7 +446,7 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
viewModel.errorMessage.observe(this) { errorMsg ->
|
viewModel.errorMessage.observe(this) { errorMsg ->
|
||||||
if (errorMsg.isNotEmpty()) {
|
if (errorMsg.isNotEmpty()) {
|
||||||
Log.e(TAG, "setupStoreTypesObserver: Error loading store types: $errorMsg")
|
Log.e(TAG, "setupStoreTypesObserver: Error loading store types: $errorMsg")
|
||||||
Toast.makeText(this, "Error loading store types: $errorMsg", Toast.LENGTH_SHORT).show()
|
// Toast.makeText(this, "Error loading store types: $errorMsg", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -848,6 +992,63 @@ class RegisterStoreActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun doUpdateStoreProfile() {
|
||||||
|
val nameBody: RequestBody = (viewModel.storeName.value ?: "")
|
||||||
|
.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
|
val typeBody: RequestBody = ((viewModel.storeTypeId.value ?: 0).toString())
|
||||||
|
.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
|
val descBody: RequestBody = (viewModel.storeDescription.value ?: "")
|
||||||
|
.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
|
val onLeaveBody: RequestBody = "false"
|
||||||
|
.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
|
|
||||||
|
// --- Build Multipart for store image (optional) ---
|
||||||
|
// Prefer compressing images to keep payload small; fall back to raw copy if needed.
|
||||||
|
val storeImgPart: MultipartBody.Part? = viewModel.storeImageUri?.let { uri ->
|
||||||
|
try {
|
||||||
|
// (A) Optional safety check: only allow jpg/png/webp
|
||||||
|
val allowed = Regex("^(jpg|jpeg|png|webp)$", RegexOption.IGNORE_CASE)
|
||||||
|
if (!ImageUtils.isAllowedFileType(this, uri, allowed)) {
|
||||||
|
Toast.makeText(this, "Format gambar tidak didukung", Toast.LENGTH_SHORT).show()
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
// (B) Compress for upload (ke cacheDir), then build multipart
|
||||||
|
val compressed: File = ImageUtils.compressImage(
|
||||||
|
context = this,
|
||||||
|
uri = uri,
|
||||||
|
filename = "storeimg", // prefix
|
||||||
|
maxWidth = 1024,
|
||||||
|
maxHeight = 1024,
|
||||||
|
quality = 80
|
||||||
|
)
|
||||||
|
FileUtils.createMultipartFromFile("storeimg", compressed)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// If compression fails, try raw copy as fallback
|
||||||
|
val rawFile = FileUtils.createTempFileFromUri(this, uri)
|
||||||
|
rawFile?.let { FileUtils.createMultipartFromFile("storeimg", it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
myStoreViewModel.updateStoreProfile(
|
||||||
|
storeName = nameBody,
|
||||||
|
storeType = typeBody,
|
||||||
|
description = descBody,
|
||||||
|
isOnLeave = onLeaveBody,
|
||||||
|
storeImage = storeImgPart
|
||||||
|
)
|
||||||
|
|
||||||
|
myStoreViewModel.updateStoreProfileResult.observe(this) {
|
||||||
|
Toast.makeText(this, "Pengajuan ulang berhasil dikirim", Toast.LENGTH_SHORT).show()
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
myStoreViewModel.errorMessage.observe(this) {
|
||||||
|
if (!it.isNullOrEmpty()) {
|
||||||
|
Toast.makeText(this, it, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "RegisterStoreActivity"
|
private const val TAG = "RegisterStoreActivity"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -91,10 +91,10 @@ class DetailStoreProfileActivity : AppCompatActivity() {
|
|||||||
viewModel.fetchStoreTypes()
|
viewModel.fetchStoreTypes()
|
||||||
|
|
||||||
viewModel.myStoreProfile.observe(this) {
|
viewModel.myStoreProfile.observe(this) {
|
||||||
currentStore = it
|
currentStore = it?.store
|
||||||
currentStoreLoaded = true
|
currentStoreLoaded = true
|
||||||
if (storeTypesLoaded) setupStoreTypeSpinner(storeTypesList)
|
if (storeTypesLoaded) setupStoreTypeSpinner(storeTypesList)
|
||||||
updateUI(it)
|
updateUI(it?.store)
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.storeTypes.observe(this) {
|
viewModel.storeTypes.observe(this) {
|
||||||
|
|||||||
@ -322,19 +322,6 @@ class DetailStoreAddressActivity : AppCompatActivity() {
|
|||||||
villageId = oldAddress.villageId
|
villageId = oldAddress.villageId
|
||||||
)
|
)
|
||||||
viewModel.saveStoreAddress(oldAddress, newAddress)
|
viewModel.saveStoreAddress(oldAddress, newAddress)
|
||||||
// Save address
|
|
||||||
// viewModel.saveStoreAddress(
|
|
||||||
// provinceId = selectedProvinceId!!,
|
|
||||||
// provinceName = province?.provinceName ?: "",
|
|
||||||
// cityId = city.cityId,
|
|
||||||
// cityName = city.cityName,
|
|
||||||
// street = street,
|
|
||||||
// subdistrict = subdistrict,
|
|
||||||
// detail = detail,
|
|
||||||
// postalCode = postalCode,
|
|
||||||
// latitude = latitude,
|
|
||||||
// longitude = longitude
|
|
||||||
// )
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -360,11 +347,13 @@ class DetailStoreAddressActivity : AppCompatActivity() {
|
|||||||
it.isEnabled = true
|
it.isEnabled = true
|
||||||
it.setBackgroundResource(R.drawable.bg_button_active)
|
it.setBackgroundResource(R.drawable.bg_button_active)
|
||||||
it.setTextColor(getColor(R.color.white))
|
it.setTextColor(getColor(R.color.white))
|
||||||
|
binding.btnSaveAddress.text = "Simpan Perubahan"
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
it.isEnabled = false
|
it.isEnabled = false
|
||||||
it.setBackgroundResource(R.drawable.bg_button_disabled)
|
it.setBackgroundResource(R.drawable.bg_button_disabled)
|
||||||
it.setTextColor(getColor(R.color.black_300))
|
it.setTextColor(getColor(R.color.black_300))
|
||||||
Toast.makeText(this, "Periksa dan lenkapi alamat anda", Toast.LENGTH_SHORT).show()
|
binding.btnSaveAddress.text = "Lengkapi alamat anda"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -97,7 +97,7 @@ class SellsAdapter(
|
|||||||
val product = order.orderItems?.firstOrNull()
|
val product = order.orderItems?.firstOrNull()
|
||||||
tvSellsProductName.text = product?.productName
|
tvSellsProductName.text = product?.productName
|
||||||
tvSellsProductQty.text = "x${product?.quantity}"
|
tvSellsProductQty.text = "x${product?.quantity}"
|
||||||
tvSellsProductPrice.text = product?.price?.let { formatPrice(it.toInt()) }
|
tvSellsProductPrice.text = product?.price?.let { formatPrice(it.toDouble().toInt()) }
|
||||||
|
|
||||||
val fullImageUrl = when (val img = product?.productImage) {
|
val fullImageUrl = when (val img = product?.productImage) {
|
||||||
is String -> {
|
is String -> {
|
||||||
@ -170,7 +170,7 @@ class SellsAdapter(
|
|||||||
val product = order.orderItems?.firstOrNull()
|
val product = order.orderItems?.firstOrNull()
|
||||||
tvSellsProductName.text = product?.productName
|
tvSellsProductName.text = product?.productName
|
||||||
tvSellsProductQty.text = "x${product?.quantity}"
|
tvSellsProductQty.text = "x${product?.quantity}"
|
||||||
tvSellsProductPrice.text = product?.price?.let { formatPrice(it.toInt()) }
|
tvSellsProductPrice.text = product?.price?.let { formatPrice(it.toDouble().toInt()) }
|
||||||
|
|
||||||
val fullImageUrl = when (val img = product?.productImage) {
|
val fullImageUrl = when (val img = product?.productImage) {
|
||||||
is String -> {
|
is String -> {
|
||||||
@ -186,7 +186,7 @@ class SellsAdapter(
|
|||||||
.into(ivSellsProduct)
|
.into(ivSellsProduct)
|
||||||
|
|
||||||
tvSellsQty.text = "${order.orderItems?.size} produk"
|
tvSellsQty.text = "${order.orderItems?.size} produk"
|
||||||
tvSellsPrice.text = order.totalAmount?.let { formatPrice(it.toInt()) }
|
tvSellsPrice.text = order.totalAmount?.let { formatPrice(it.toDouble().toInt()) }
|
||||||
}
|
}
|
||||||
"paid" -> {
|
"paid" -> {
|
||||||
layoutOrders.visibility = View.GONE
|
layoutOrders.visibility = View.GONE
|
||||||
|
|||||||
@ -5,9 +5,15 @@ import android.view.LayoutInflater
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.activityViewModels
|
||||||
|
import androidx.viewpager2.widget.ViewPager2
|
||||||
import com.alya.ecommerce_serang.R
|
import com.alya.ecommerce_serang.R
|
||||||
|
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||||
|
import com.alya.ecommerce_serang.data.repository.SellsRepository
|
||||||
import com.alya.ecommerce_serang.databinding.FragmentSellsBinding
|
import com.alya.ecommerce_serang.databinding.FragmentSellsBinding
|
||||||
|
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||||
import com.alya.ecommerce_serang.utils.SessionManager
|
import com.alya.ecommerce_serang.utils.SessionManager
|
||||||
|
import com.alya.ecommerce_serang.utils.viewmodel.SellsViewModel
|
||||||
import com.google.android.material.tabs.TabLayoutMediator
|
import com.google.android.material.tabs.TabLayoutMediator
|
||||||
|
|
||||||
class SellsFragment : Fragment() {
|
class SellsFragment : Fragment() {
|
||||||
@ -18,6 +24,13 @@ class SellsFragment : Fragment() {
|
|||||||
|
|
||||||
private lateinit var viewPagerAdapter: SellsViewPagerAdapter
|
private lateinit var viewPagerAdapter: SellsViewPagerAdapter
|
||||||
|
|
||||||
|
private val sellsVm: SellsViewModel by activityViewModels {
|
||||||
|
BaseViewModelFactory {
|
||||||
|
val api = ApiConfig.getApiService(SessionManager(requireContext()))
|
||||||
|
SellsViewModel(SellsRepository(api))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
@ -53,8 +66,25 @@ class SellsFragment : Fragment() {
|
|||||||
else -> "Tab $position"
|
else -> "Tab $position"
|
||||||
}
|
}
|
||||||
}.attach()
|
}.attach()
|
||||||
|
|
||||||
|
statusPage()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun statusPage(){
|
||||||
|
binding.viewPagerSells.registerOnPageChangeCallback(
|
||||||
|
object : ViewPager2.OnPageChangeCallback() {
|
||||||
|
override fun onPageSelected(position: Int) {
|
||||||
|
val status = viewPagerAdapter.sellsStatuses[position]
|
||||||
|
sellsVm.updateStatus(status, forceRefresh = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
statusPage()
|
||||||
|
}
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
_binding = null
|
_binding = null
|
||||||
|
|||||||
@ -83,11 +83,10 @@ class SellsListFragment : Fragment() {
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
loadSells()
|
||||||
setupRecyclerView()
|
setupRecyclerView()
|
||||||
observeSellsList()
|
observeSellsList()
|
||||||
observePaymentConfirmation()
|
observePaymentConfirmation()
|
||||||
loadSells()
|
|
||||||
// getAllOrderCountsAndNavigate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupRecyclerView() {
|
private fun setupRecyclerView() {
|
||||||
@ -121,8 +120,8 @@ class SellsListFragment : Fragment() {
|
|||||||
Log.d(TAG, "Data received: ${result.data?.size ?: 0} items")
|
Log.d(TAG, "Data received: ${result.data?.size ?: 0} items")
|
||||||
|
|
||||||
if (result.data.isNullOrEmpty()) {
|
if (result.data.isNullOrEmpty()) {
|
||||||
binding.tvEmptyState.visibility = View.VISIBLE
|
|
||||||
binding.rvSells.visibility = View.GONE
|
binding.rvSells.visibility = View.GONE
|
||||||
|
binding.tvEmptyState.visibility = View.VISIBLE
|
||||||
Log.d(TAG, "Showing empty state")
|
Log.d(TAG, "Showing empty state")
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -211,6 +210,12 @@ class SellsListFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
viewModel.getSellList(status)
|
||||||
|
observeSellsList()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
_binding = null
|
_binding = null
|
||||||
|
|||||||
@ -9,7 +9,7 @@ class SellsViewPagerAdapter(
|
|||||||
) : FragmentStateAdapter(fragmentActivity) {
|
) : FragmentStateAdapter(fragmentActivity) {
|
||||||
|
|
||||||
// Define all possible sells statuses - keeping your original list
|
// Define all possible sells statuses - keeping your original list
|
||||||
private val sellsStatuses = listOf(
|
val sellsStatuses = listOf(
|
||||||
"all",
|
"all",
|
||||||
"unpaid",
|
"unpaid",
|
||||||
"paid",
|
"paid",
|
||||||
|
|||||||
@ -2,7 +2,6 @@ package com.alya.ecommerce_serang.ui.profile.mystore.sells.payment
|
|||||||
|
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.drawable.ColorDrawable
|
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
@ -16,6 +15,8 @@ import android.widget.TextView
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.graphics.drawable.toDrawable
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.alya.ecommerce_serang.BuildConfig.BASE_URL
|
import com.alya.ecommerce_serang.BuildConfig.BASE_URL
|
||||||
import com.alya.ecommerce_serang.R
|
import com.alya.ecommerce_serang.R
|
||||||
import com.alya.ecommerce_serang.data.api.response.store.sells.Orders
|
import com.alya.ecommerce_serang.data.api.response.store.sells.Orders
|
||||||
@ -38,9 +39,6 @@ import java.text.SimpleDateFormat
|
|||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.TimeZone
|
import java.util.TimeZone
|
||||||
import androidx.core.graphics.drawable.toDrawable
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import com.alya.ecommerce_serang.ui.profile.mystore.sells.DetailSellsActivity
|
|
||||||
|
|
||||||
class DetailPaymentActivity : AppCompatActivity() {
|
class DetailPaymentActivity : AppCompatActivity() {
|
||||||
|
|
||||||
@ -139,6 +137,7 @@ class DetailPaymentActivity : AppCompatActivity() {
|
|||||||
tvOrderSubtotal.text = formatPrice(sell.totalAmount.toString())
|
tvOrderSubtotal.text = formatPrice(sell.totalAmount.toString())
|
||||||
tvOrderShipPrice.text = formatPrice(sell.shipmentPrice.toString())
|
tvOrderShipPrice.text = formatPrice(sell.shipmentPrice.toString())
|
||||||
tvOrderPrice.text = formatPrice(sell.totalAmount.toString())
|
tvOrderPrice.text = formatPrice(sell.totalAmount.toString())
|
||||||
|
tvOrderDue.text = formatDueDate(sell.updatedAt.toString(), 2)
|
||||||
tvOrderRecipient.text = sell.recipient ?: "-"
|
tvOrderRecipient.text = sell.recipient ?: "-"
|
||||||
tvOrderRecipientNum.text = sell.receiptNum?.toString() ?: "-"
|
tvOrderRecipientNum.text = sell.receiptNum?.toString() ?: "-"
|
||||||
|
|
||||||
@ -201,6 +200,28 @@ class DetailPaymentActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun formatDueDate(dateString: String, dueDay: Int): String {
|
||||||
|
return try {
|
||||||
|
val inputFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault())
|
||||||
|
inputFormat.timeZone = TimeZone.getTimeZone("UTC")
|
||||||
|
|
||||||
|
val outputFormat = SimpleDateFormat("dd MMM; HH.mm", Locale("id", "ID"))
|
||||||
|
|
||||||
|
val date = inputFormat.parse(dateString)
|
||||||
|
|
||||||
|
date?.let {
|
||||||
|
val calendar = Calendar.getInstance()
|
||||||
|
calendar.time = it
|
||||||
|
calendar.add(Calendar.DATE, dueDay)
|
||||||
|
|
||||||
|
outputFormat.format(calendar.time)
|
||||||
|
} ?: dateString
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("DateFormatting", "Error formatting date: ${e.message}")
|
||||||
|
dateString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun formatPrice(price: String): String {
|
private fun formatPrice(price: String): String {
|
||||||
val priceDouble = price.toDoubleOrNull() ?: 0.0
|
val priceDouble = price.toDoubleOrNull() ?: 0.0
|
||||||
return String.format(Locale("id", "ID"), "Rp%,.0f", priceDouble)
|
return String.format(Locale("id", "ID"), "Rp%,.0f", priceDouble)
|
||||||
|
|||||||
@ -14,7 +14,6 @@ import com.alya.ecommerce_serang.data.repository.AddressRepository
|
|||||||
import com.alya.ecommerce_serang.data.repository.SellsRepository
|
import com.alya.ecommerce_serang.data.repository.SellsRepository
|
||||||
import com.alya.ecommerce_serang.databinding.ActivityDetailShipmentBinding
|
import com.alya.ecommerce_serang.databinding.ActivityDetailShipmentBinding
|
||||||
import com.alya.ecommerce_serang.ui.profile.mystore.sells.SellsProductAdapter
|
import com.alya.ecommerce_serang.ui.profile.mystore.sells.SellsProductAdapter
|
||||||
import com.alya.ecommerce_serang.ui.profile.mystore.sells.payment.DetailPaymentActivity
|
|
||||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||||
import com.alya.ecommerce_serang.utils.SessionManager
|
import com.alya.ecommerce_serang.utils.SessionManager
|
||||||
import com.alya.ecommerce_serang.utils.viewmodel.AddressViewModel
|
import com.alya.ecommerce_serang.utils.viewmodel.AddressViewModel
|
||||||
@ -24,7 +23,6 @@ import java.text.SimpleDateFormat
|
|||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.TimeZone
|
import java.util.TimeZone
|
||||||
import kotlin.getValue
|
|
||||||
|
|
||||||
class DetailShipmentActivity : AppCompatActivity() {
|
class DetailShipmentActivity : AppCompatActivity() {
|
||||||
|
|
||||||
@ -106,6 +104,7 @@ class DetailShipmentActivity : AppCompatActivity() {
|
|||||||
tvOrderSubtotal.text = formatPrice(sell.totalAmount.toString())
|
tvOrderSubtotal.text = formatPrice(sell.totalAmount.toString())
|
||||||
tvOrderShipPrice.text = formatPrice(sell.shipmentPrice.toString())
|
tvOrderShipPrice.text = formatPrice(sell.shipmentPrice.toString())
|
||||||
tvOrderPrice.text = formatPrice(sell.totalAmount.toString())
|
tvOrderPrice.text = formatPrice(sell.totalAmount.toString())
|
||||||
|
tvOrderDue.text = formatDueDate(sell.updatedAt.toString(), 2)
|
||||||
tvOrderRecipient.text = sell.recipient ?: "-"
|
tvOrderRecipient.text = sell.recipient ?: "-"
|
||||||
tvOrderRecipientNum.text = sell.receiptNum?.toString() ?: "-"
|
tvOrderRecipientNum.text = sell.receiptNum?.toString() ?: "-"
|
||||||
|
|
||||||
@ -168,6 +167,28 @@ class DetailShipmentActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun formatDueDate(dateString: String, dueDay: Int): String {
|
||||||
|
return try {
|
||||||
|
val inputFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault())
|
||||||
|
inputFormat.timeZone = TimeZone.getTimeZone("UTC")
|
||||||
|
|
||||||
|
val outputFormat = SimpleDateFormat("dd MMM; HH.mm", Locale("id", "ID"))
|
||||||
|
|
||||||
|
val date = inputFormat.parse(dateString)
|
||||||
|
|
||||||
|
date?.let {
|
||||||
|
val calendar = Calendar.getInstance()
|
||||||
|
calendar.time = it
|
||||||
|
calendar.add(Calendar.DATE, dueDay)
|
||||||
|
|
||||||
|
outputFormat.format(calendar.time)
|
||||||
|
} ?: dateString
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("DateFormatting", "Error formatting date: ${e.message}")
|
||||||
|
dateString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun formatPrice(price: String): String {
|
private fun formatPrice(price: String): String {
|
||||||
val priceDouble = price.toDoubleOrNull() ?: 0.0
|
val priceDouble = price.toDoubleOrNull() ?: 0.0
|
||||||
return String.format(Locale("id", "ID"), "Rp%,.0f", priceDouble)
|
return String.format(Locale("id", "ID"), "Rp%,.0f", priceDouble)
|
||||||
|
|||||||
@ -10,6 +10,8 @@ import com.alya.ecommerce_serang.data.api.dto.ProductsItem
|
|||||||
import com.alya.ecommerce_serang.data.api.dto.Store
|
import com.alya.ecommerce_serang.data.api.dto.Store
|
||||||
import com.alya.ecommerce_serang.data.api.response.auth.StoreTypesItem
|
import com.alya.ecommerce_serang.data.api.response.auth.StoreTypesItem
|
||||||
import com.alya.ecommerce_serang.data.api.response.store.StoreResponse
|
import com.alya.ecommerce_serang.data.api.response.store.StoreResponse
|
||||||
|
import com.alya.ecommerce_serang.data.api.response.store.profile.Payment
|
||||||
|
import com.alya.ecommerce_serang.data.api.response.store.profile.Shipping
|
||||||
import com.alya.ecommerce_serang.data.api.response.store.profile.StoreDataResponse
|
import com.alya.ecommerce_serang.data.api.response.store.profile.StoreDataResponse
|
||||||
import com.alya.ecommerce_serang.data.repository.MyStoreRepository
|
import com.alya.ecommerce_serang.data.repository.MyStoreRepository
|
||||||
import com.alya.ecommerce_serang.data.repository.Result
|
import com.alya.ecommerce_serang.data.repository.Result
|
||||||
@ -23,12 +25,18 @@ import java.util.Locale
|
|||||||
class MyStoreViewModel(private val repository: MyStoreRepository): ViewModel() {
|
class MyStoreViewModel(private val repository: MyStoreRepository): ViewModel() {
|
||||||
private var TAG = "MyStoreViewModel"
|
private var TAG = "MyStoreViewModel"
|
||||||
|
|
||||||
private val _myStoreProfile = MutableLiveData<Store?>()
|
private val _myStoreProfile = MutableLiveData<StoreResponse?>()
|
||||||
val myStoreProfile: LiveData<Store?> = _myStoreProfile
|
val myStoreProfile: LiveData<StoreResponse?> = _myStoreProfile
|
||||||
|
|
||||||
private val _storeTypes = MutableLiveData<List<StoreTypesItem>>()
|
private val _storeTypes = MutableLiveData<List<StoreTypesItem>>()
|
||||||
val storeTypes: LiveData<List<StoreTypesItem>> = _storeTypes
|
val storeTypes: LiveData<List<StoreTypesItem>> = _storeTypes
|
||||||
|
|
||||||
|
private val _shipping = MutableLiveData<List<Shipping>>()
|
||||||
|
val shipping: LiveData<List<Shipping>> = _shipping
|
||||||
|
|
||||||
|
private val _payment = MutableLiveData<List<Payment>>()
|
||||||
|
val payment: LiveData<List<Payment>> = _payment
|
||||||
|
|
||||||
private val _isLoadingType = MutableLiveData<Boolean>()
|
private val _isLoadingType = MutableLiveData<Boolean>()
|
||||||
val isLoadingType: LiveData<Boolean> = _isLoadingType
|
val isLoadingType: LiveData<Boolean> = _isLoadingType
|
||||||
|
|
||||||
@ -47,7 +55,12 @@ class MyStoreViewModel(private val repository: MyStoreRepository): ViewModel() {
|
|||||||
fun loadMyStore(){
|
fun loadMyStore(){
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
when (val result = repository.fetchMyStoreProfile()){
|
when (val result = repository.fetchMyStoreProfile()){
|
||||||
is Result.Success -> _myStoreProfile.postValue(result.data)
|
is Result.Success -> {
|
||||||
|
val storeData = result.data
|
||||||
|
_myStoreProfile.postValue(storeData)
|
||||||
|
_shipping.postValue(storeData?.shipping)
|
||||||
|
_payment.postValue(storeData?.payment)
|
||||||
|
}
|
||||||
is Result.Error -> _errorMessage.postValue(result.exception.message ?: "Unknown Error")
|
is Result.Error -> _errorMessage.postValue(result.exception.message ?: "Unknown Error")
|
||||||
is Result.Loading -> null
|
is Result.Loading -> null
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import androidx.lifecycle.MutableLiveData
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.alya.ecommerce_serang.data.api.dto.UserProfile
|
import com.alya.ecommerce_serang.data.api.dto.UserProfile
|
||||||
|
import com.alya.ecommerce_serang.data.api.response.auth.ChangePassResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.auth.HasStoreResponse
|
import com.alya.ecommerce_serang.data.api.response.auth.HasStoreResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.customer.profile.EditProfileResponse
|
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.Result
|
||||||
@ -26,7 +27,7 @@ class ProfileViewModel(private val userRepository: UserRepository) : ViewModel()
|
|||||||
|
|
||||||
private val _checkStore = MutableLiveData<Boolean>()
|
private val _checkStore = MutableLiveData<Boolean>()
|
||||||
val checkStore: LiveData<Boolean> = _checkStore
|
val checkStore: LiveData<Boolean> = _checkStore
|
||||||
|
val changePasswordResult = MutableLiveData<Result<ChangePassResponse>>()
|
||||||
private val _logout = MutableLiveData<Boolean>()
|
private val _logout = MutableLiveData<Boolean>()
|
||||||
val logout : LiveData<Boolean> = _logout
|
val logout : LiveData<Boolean> = _logout
|
||||||
|
|
||||||
@ -110,6 +111,20 @@ class ProfileViewModel(private val userRepository: UserRepository) : ViewModel()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun changePassword(currentPassword: String, newPassword: String) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
// Call the repository to change the password
|
||||||
|
val result = userRepository.changePassword(currentPassword, newPassword)
|
||||||
|
|
||||||
|
// Post the result (success or error) to LiveData
|
||||||
|
changePasswordResult.postValue(result)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// Handle any unexpected errors
|
||||||
|
changePasswordResult.postValue(Result.Error(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "ProfileViewModel"
|
private const val TAG = "ProfileViewModel"
|
||||||
|
|||||||
@ -7,9 +7,11 @@ import androidx.lifecycle.MutableLiveData
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.alya.ecommerce_serang.data.api.dto.CreateAddressRequest
|
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.RegisterRequest
|
import com.alya.ecommerce_serang.data.api.dto.RegisterRequest
|
||||||
import com.alya.ecommerce_serang.data.api.dto.ResetPassReq
|
import com.alya.ecommerce_serang.data.api.dto.ResetPassReq
|
||||||
import com.alya.ecommerce_serang.data.api.dto.VerifRegisReq
|
import com.alya.ecommerce_serang.data.api.dto.VerifRegisReq
|
||||||
|
import com.alya.ecommerce_serang.data.api.response.auth.FcmTokenResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.auth.LoginResponse
|
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.OtpResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.auth.RegisterResponse
|
import com.alya.ecommerce_serang.data.api.response.auth.RegisterResponse
|
||||||
@ -388,6 +390,34 @@ class RegisterViewModel(private val repository: UserRepository, private val orde
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun sendFcm(token: FcmReq) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_otpState.value = Result.Loading // Indicating API call in progress
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Call the repository function to request OTP
|
||||||
|
val authenticatedApiService = getAuthenticatedApiService()
|
||||||
|
val authenticatedOrderRepo = UserRepository(authenticatedApiService)
|
||||||
|
val response: FcmTokenResponse = authenticatedOrderRepo.sendFcm(token)
|
||||||
|
|
||||||
|
// Log and store success message
|
||||||
|
Log.d("LoginViewModel", "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("LoginViewModel", "OTP request failed for: $token", exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "RegisterViewModel"
|
private const val TAG = "RegisterViewModel"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,9 @@ import com.alya.ecommerce_serang.ui.order.address.ViewState
|
|||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.awaitAll
|
import kotlinx.coroutines.awaitAll
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
@ -52,6 +55,9 @@ class SellsViewModel(private val repository: SellsRepository) : ViewModel() {
|
|||||||
private val _error = MutableLiveData<String>()
|
private val _error = MutableLiveData<String>()
|
||||||
val error: LiveData<String> get() = _error
|
val error: LiveData<String> get() = _error
|
||||||
|
|
||||||
|
private val _selectedStatus = MutableStateFlow("all")
|
||||||
|
val selectedStatus: StateFlow<String> = _selectedStatus.asStateFlow()
|
||||||
|
|
||||||
fun getSellList(status: String) {
|
fun getSellList(status: String) {
|
||||||
Log.d(TAG, "========== Starting getSellList ==========")
|
Log.d(TAG, "========== Starting getSellList ==========")
|
||||||
Log.d(TAG, "Requested status: '$status'")
|
Log.d(TAG, "Requested status: '$status'")
|
||||||
@ -320,4 +326,40 @@ class SellsViewModel(private val repository: SellsRepository) : ViewModel() {
|
|||||||
|
|
||||||
Log.d(TAG, "========== refreshOrders method completed ==========")
|
Log.d(TAG, "========== refreshOrders method completed ==========")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun updateStatus(status: String, forceRefresh: Boolean = false) {
|
||||||
|
Log.d(TAG, "↪️ updateStatus(status = $status, forceRefresh = $forceRefresh)")
|
||||||
|
|
||||||
|
// No‑op guard (optional): skip if user re‑selects same tab and no refresh asked
|
||||||
|
if (_selectedStatus.value == status && !forceRefresh) {
|
||||||
|
Log.d(TAG, "🔸 Status unchanged & forceRefresh = false → skip update")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_selectedStatus.value = status
|
||||||
|
Log.d(TAG, "✅ _selectedStatus set to \"$status\"")
|
||||||
|
|
||||||
|
if (forceRefresh) {
|
||||||
|
Log.d(TAG, "🔄 forceRefresh = true → launching refresh()")
|
||||||
|
viewModelScope.launch { refresh(status) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun refresh(status: String) {
|
||||||
|
Log.d(TAG, "⏳ refresh(\"$status\") started")
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (status == "all") {
|
||||||
|
Log.d(TAG, "🌐 Calling getAllOrdersCombined()")
|
||||||
|
getAllStatusCounts() // network → cache
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "🌐 repository.getOrderList(\"$status\")")
|
||||||
|
repository.getSellList(status) // network → cache
|
||||||
|
}
|
||||||
|
Log.d(TAG, "✅ refresh(\"$status\") completed (repository updated)")
|
||||||
|
// Flow that watches DB/cache will emit automatically
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "❌ refresh(\"$status\") failed: ${e.message}", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="100dp" android:tint="#489EC6" android:viewportHeight="24" android:viewportWidth="24" android:width="100dp">
|
||||||
|
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10s10,-4.48 10,-10S17.52,2 12,2zM12,6c1.93,0 3.5,1.57 3.5,3.5S13.93,13 12,13s-3.5,-1.57 -3.5,-3.5S10.07,6 12,6zM12,20c-2.03,0 -4.43,-0.82 -6.14,-2.88C7.55,15.8 9.68,15 12,15s4.45,0.8 6.14,2.12C16.43,19.18 14.03,20 12,20z"/>
|
||||||
|
|
||||||
|
</vector>
|
||||||
5
app/src/main/res/drawable/baseline_circle_24.xml
Normal file
5
app/src/main/res/drawable/baseline_circle_24.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#489EC6" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||||
|
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2z"/>
|
||||||
|
|
||||||
|
</vector>
|
||||||
@ -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="M7,18c-1.1,0 -1.99,0.9 -1.99,2S5.9,22 7,22s2,-0.9 2,-2 -0.9,-2 -2,-2zM1,2v2h2l3.6,7.59 -1.35,2.45c-0.16,0.28 -0.25,0.61 -0.25,0.96 0,1.1 0.9,2 2,2h12v-2L7.42,15c-0.14,0 -0.25,-0.11 -0.25,-0.25l0.03,-0.12 0.9,-1.63h7.45c0.75,0 1.41,-0.41 1.75,-1.03l3.58,-6.49c0.08,-0.14 0.12,-0.31 0.12,-0.48 0,-0.55 -0.45,-1 -1,-1L5.21,4l-0.94,-2L1,2zM17,18c-1.1,0 -1.99,0.9 -1.99,2s0.89,2 1.99,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
|
||||||
|
|
||||||
|
</vector>
|
||||||
5
app/src/main/res/drawable/baseline_payment_24.xml
Normal file
5
app/src/main/res/drawable/baseline_payment_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="M20,4L4,4c-1.11,0 -1.99,0.89 -1.99,2L2,18c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,6c0,-1.11 -0.89,-2 -2,-2zM20,18L4,18v-6h16v6zM20,8L4,8L4,6h16v2z"/>
|
||||||
|
|
||||||
|
</vector>
|
||||||
@ -3,6 +3,6 @@
|
|||||||
android:shape="rectangle">
|
android:shape="rectangle">
|
||||||
|
|
||||||
<solid android:color="@color/blue_500" />
|
<solid android:color="@color/blue_500" />
|
||||||
<corners android:radius="5dp" />
|
<corners android:radius="24dp" />
|
||||||
|
|
||||||
</shape>
|
</shape>
|
||||||
|
|||||||
BIN
app/src/main/res/drawable/ic_change_pass.png
Normal file
BIN
app/src/main/res/drawable/ic_change_pass.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.8 KiB |
29
app/src/main/res/drawable/logo_bisa_umkm.xml
Normal file
29
app/src/main/res/drawable/logo_bisa_umkm.xml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="64dp"
|
||||||
|
android:height="64dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
|
||||||
|
<!-- Background circle for better splash screen appearance -->
|
||||||
|
<path
|
||||||
|
android:pathData="M54,54m-50,0a50,50 0,1 1,100 0a50,50 0,1 1,-100 0"
|
||||||
|
android:fillColor="#ffff"/>
|
||||||
|
|
||||||
|
<!-- Main logo content scaled and centered -->
|
||||||
|
<group android:translateX="32" android:translateY="32" android:scaleX="0.7" android:scaleY="0.7">
|
||||||
|
<!-- Background rectangle -->
|
||||||
|
<path
|
||||||
|
android:pathData="M0,0h64v64h-64z"
|
||||||
|
android:fillColor="#489EC6"/>
|
||||||
|
|
||||||
|
<!-- Main logo shape -->
|
||||||
|
<path
|
||||||
|
android:pathData="M11.868,58C10.523,58 9.399,57.538 8.497,56.614C7.594,55.69 7.144,54.537 7.146,53.155V29.074C5.912,28.14 5.009,26.915 4.438,25.399C3.867,23.883 3.854,22.266 4.4,20.548L7.245,10.924C7.635,9.7 8.264,8.74 9.131,8.044C10.001,7.348 11.075,7 12.354,7H51.536C52.813,7 53.883,7.326 54.747,7.978C55.608,8.63 56.24,9.573 56.641,10.807L59.598,20.545C60.146,22.265 60.134,23.896 59.563,25.438C58.992,26.98 58.089,28.23 56.855,29.188V53.152C56.855,54.534 56.404,55.687 55.501,56.611C54.599,57.535 53.476,57.998 52.133,58H11.868ZM38.433,28C40.311,28 41.712,27.46 42.638,26.38C43.564,25.302 43.954,24.188 43.808,23.038L41.866,10H33.465V22.6C33.465,24.074 33.957,25.342 34.939,26.404C35.922,27.468 37.084,28 38.433,28ZM25.278,28C26.849,28 28.119,27.468 29.088,26.404C30.057,25.34 30.541,24.072 30.541,22.6V10H22.138L20.19,23.269C20.071,24.201 20.468,25.222 21.38,26.332C22.292,27.442 23.594,27.998 25.278,28ZM12.263,28C13.551,28 14.647,27.55 15.55,26.65C16.452,25.75 17.014,24.627 17.234,23.281L19.067,10H12.354C11.716,10 11.209,10.144 10.833,10.432C10.457,10.72 10.176,11.153 9.991,11.731L7.292,21.205C6.812,22.781 7.017,24.308 7.906,25.786C8.795,27.264 10.247,28.002 12.263,28ZM51.738,28C53.488,28 54.886,27.3 55.931,25.9C56.978,24.5 57.237,22.935 56.709,21.205L53.864,11.617C53.676,11.039 53.395,10.625 53.019,10.375C52.642,10.125 52.137,10 51.501,10H44.933L46.767,23.281C46.987,24.627 47.549,25.75 48.451,26.65C49.354,27.55 50.449,28 51.738,28Z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
|
||||||
|
<!-- Text elements -->
|
||||||
|
<path
|
||||||
|
android:pathData="M20.382,39V30.6H23.97C24.554,30.6 25.046,30.692 25.446,30.876C25.846,31.052 26.15,31.304 26.358,31.632C26.574,31.952 26.682,32.332 26.682,32.772C26.682,33.196 26.59,33.552 26.406,33.84C26.222,34.128 25.978,34.348 25.674,34.5C25.378,34.652 25.05,34.744 24.69,34.776L24.882,34.632C25.274,34.648 25.618,34.752 25.914,34.944C26.21,35.136 26.442,35.388 26.61,35.7C26.786,36.004 26.874,36.34 26.874,36.708C26.874,37.164 26.766,37.564 26.55,37.908C26.334,38.252 26.018,38.52 25.602,38.712C25.186,38.904 24.682,39 24.09,39H20.382ZM22.182,37.536H23.79C24.19,37.536 24.498,37.448 24.714,37.272C24.938,37.088 25.05,36.824 25.05,36.48C25.05,36.136 24.934,35.868 24.702,35.676C24.478,35.484 24.166,35.388 23.766,35.388H22.182V37.536ZM22.182,34.056H23.658C24.042,34.056 24.334,33.968 24.534,33.792C24.742,33.616 24.846,33.368 24.846,33.048C24.846,32.728 24.742,32.48 24.534,32.304C24.334,32.12 24.038,32.028 23.646,32.028H22.182V34.056ZM28.163,39V32.952H29.963V39H28.163ZM29.063,32.256C28.743,32.256 28.483,32.164 28.283,31.98C28.083,31.796 27.983,31.564 27.983,31.284C27.983,30.996 28.083,30.76 28.283,30.576C28.483,30.392 28.743,30.3 29.063,30.3C29.391,30.3 29.655,30.392 29.855,30.576C30.063,30.76 30.167,30.996 30.167,31.284C30.167,31.564 30.063,31.796 29.855,31.98C29.655,32.164 29.391,32.256 29.063,32.256ZM34.069,39.144C33.501,39.144 33.009,39.056 32.593,38.88C32.186,38.696 31.861,38.448 31.622,38.136C31.389,37.824 31.257,37.472 31.226,37.08H33.014C33.046,37.216 33.102,37.34 33.181,37.452C33.27,37.556 33.386,37.64 33.529,37.704C33.681,37.76 33.849,37.788 34.034,37.788C34.234,37.788 34.394,37.764 34.514,37.716C34.641,37.66 34.737,37.588 34.801,37.5C34.866,37.412 34.897,37.32 34.897,37.224C34.897,37.072 34.849,36.956 34.754,36.876C34.666,36.796 34.534,36.732 34.357,36.684C34.181,36.628 33.97,36.576 33.722,36.528C33.433,36.464 33.146,36.392 32.857,36.312C32.577,36.224 32.326,36.116 32.102,35.988C31.885,35.86 31.709,35.696 31.573,35.496C31.445,35.288 31.382,35.036 31.382,34.74C31.382,34.38 31.482,34.056 31.681,33.768C31.882,33.472 32.169,33.24 32.546,33.072C32.922,32.896 33.377,32.808 33.914,32.808C34.674,32.808 35.27,32.976 35.701,33.312C36.133,33.648 36.389,34.1 36.47,34.668H34.79C34.742,34.508 34.641,34.388 34.489,34.308C34.338,34.22 34.146,34.176 33.914,34.176C33.65,34.176 33.45,34.22 33.313,34.308C33.178,34.396 33.11,34.512 33.11,34.656C33.11,34.752 33.153,34.84 33.242,34.92C33.338,34.992 33.473,35.056 33.65,35.112C33.826,35.168 34.042,35.224 34.298,35.28C34.785,35.384 35.206,35.496 35.557,35.616C35.917,35.736 36.197,35.912 36.397,36.144C36.597,36.368 36.694,36.696 36.686,37.128C36.694,37.52 36.59,37.868 36.374,38.172C36.166,38.476 35.866,38.716 35.473,38.892C35.082,39.06 34.613,39.144 34.069,39.144ZM40.094,39.144C39.59,39.144 39.17,39.064 38.834,38.904C38.506,38.744 38.262,38.528 38.102,38.256C37.95,37.976 37.874,37.668 37.874,37.332C37.874,36.972 37.962,36.656 38.138,36.384C38.322,36.104 38.606,35.884 38.99,35.724C39.374,35.556 39.858,35.472 40.442,35.472H41.906C41.906,35.2 41.87,34.976 41.798,34.8C41.734,34.624 41.626,34.492 41.474,34.404C41.322,34.316 41.114,34.272 40.85,34.272C40.57,34.272 40.334,34.328 40.142,34.44C39.95,34.552 39.83,34.728 39.782,34.968H38.054C38.094,34.536 38.234,34.16 38.474,33.84C38.722,33.52 39.05,33.268 39.458,33.084C39.866,32.9 40.334,32.808 40.862,32.808C41.438,32.808 41.938,32.904 42.362,33.096C42.786,33.28 43.114,33.552 43.346,33.912C43.586,34.272 43.706,34.72 43.706,35.256V39H42.206L41.99,38.124C41.902,38.276 41.798,38.416 41.678,38.544C41.558,38.664 41.418,38.772 41.258,38.868C41.098,38.956 40.922,39.024 40.73,39.072C40.538,39.12 40.326,39.144 40.094,39.144ZM40.538,37.776C40.73,37.776 40.898,37.744 41.042,37.68C41.186,37.616 41.31,37.528 41.414,37.416C41.518,37.304 41.602,37.176 41.666,37.032C41.738,36.88 41.79,36.716 41.822,36.54V36.528H40.658C40.458,36.528 40.29,36.556 40.154,36.612C40.026,36.66 39.93,36.732 39.866,36.828C39.802,36.924 39.77,37.036 39.77,37.164C39.77,37.3 39.802,37.416 39.866,37.512C39.938,37.6 40.03,37.668 40.142,37.716C40.262,37.756 40.394,37.776 40.538,37.776ZM17.132,53.144C16.5,53.144 15.924,53.02 15.404,52.772C14.892,52.516 14.484,52.136 14.18,51.632C13.884,51.128 13.736,50.492 13.736,49.724V44.6H15.536V49.736C15.536,50.112 15.596,50.432 15.716,50.696C15.844,50.96 16.028,51.16 16.268,51.296C16.516,51.424 16.812,51.488 17.156,51.488C17.508,51.488 17.804,51.424 18.044,51.296C18.292,51.16 18.48,50.96 18.608,50.696C18.736,50.432 18.8,50.112 18.8,49.736V44.6H20.6V49.724C20.6,50.492 20.44,51.128 20.12,51.632C19.808,52.136 19.388,52.516 18.86,52.772C18.34,53.02 17.764,53.144 17.132,53.144ZM22.128,53V44.6H24.288L26.736,49.652L29.16,44.6H31.308V53H29.508V47.636L27.444,51.824H26.004L23.928,47.636V53H22.128ZM32.921,53V44.6H34.721V47.78L37.625,44.6H39.833L36.749,47.924L39.941,53H37.733L35.465,49.316L34.721,50.12V53H32.921ZM41.007,53V44.6H43.167L45.615,49.652L48.039,44.6H50.187V53H48.387V47.636L46.323,51.824H44.883L42.807,47.636V53H41.007Z"
|
||||||
|
android:fillColor="#489EC6"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
@ -113,6 +113,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
|
android:visibility="gone"
|
||||||
android:text="Metode Pembayaran *"
|
android:text="Metode Pembayaran *"
|
||||||
android:fontFamily="@font/dmsans_semibold"
|
android:fontFamily="@font/dmsans_semibold"
|
||||||
android:textSize="16sp" />
|
android:textSize="16sp" />
|
||||||
@ -122,6 +123,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
|
android:visibility="gone"
|
||||||
android:background="@drawable/edit_text_background"
|
android:background="@drawable/edit_text_background"
|
||||||
android:minHeight="50dp"
|
android:minHeight="50dp"
|
||||||
android:padding="12dp" />
|
android:padding="12dp" />
|
||||||
|
|||||||
102
app/src/main/res/layout/activity_change_password.xml
Normal file
102
app/src/main/res/layout/activity_change_password.xml
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/main"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
tools:context=".ui.profile.ChangePasswordActivity">
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/headerStoreProduct"
|
||||||
|
layout="@layout/header" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingHorizontal="32dp"
|
||||||
|
android:paddingVertical="16dp">
|
||||||
|
|
||||||
|
<!-- Password label-->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_password_label"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fontFamily="@font/dmsans_medium"
|
||||||
|
android:text="Kata Sandi Lama"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:layout_marginVertical="8dp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
<!-- Password input -->
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/til_login_password"
|
||||||
|
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
app:passwordToggleEnabled="true"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/tv_password_label"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/et_login_password"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Masukkan kata sandi akun"
|
||||||
|
android:inputType="textPassword" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<!-- Password label-->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_new_password_label"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fontFamily="@font/dmsans_medium"
|
||||||
|
android:text="Kata Sandi Baru"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:layout_marginVertical="8dp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/til_login_password"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
<!-- Password input -->
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/til_login_new_password"
|
||||||
|
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
app:passwordToggleEnabled="true"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/tv_new_password_label"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/et_login_new_password"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Masukkan kata sandi baru"
|
||||||
|
android:inputType="textPassword" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<!-- Change Pass button -->
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btn_change_pass"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Ubah Kata Sandi"
|
||||||
|
app:cornerRadius="8dp"
|
||||||
|
android:layout_marginVertical="16dp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/til_login_new_password" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@ -34,69 +34,50 @@
|
|||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<!-- Delivery Address Section -->
|
<!-- Delivery Address Section -->
|
||||||
<androidx.cardview.widget.CardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:id="@+id/card_delivery_address"
|
android:id="@+id/card_delivery_address"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginHorizontal="0dp"
|
android:layout_marginHorizontal="16dp"
|
||||||
android:layout_marginTop="0dp"
|
android:layout_marginTop="16dp"
|
||||||
app:cardElevation="0dp">
|
app:cardCornerRadius="12dp"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardBackgroundColor="@color/white"
|
||||||
|
app:strokeColor="#E0E0E0"
|
||||||
|
app:strokeWidth="1dp">
|
||||||
|
|
||||||
<LinearLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:padding="20dp">
|
||||||
android:padding="16dp"
|
|
||||||
android:background="@color/white">
|
|
||||||
|
|
||||||
|
<!-- Header Row -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:id="@+id/address_header"
|
||||||
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/iv_location_icon"
|
android:id="@+id/iv_location_icon"
|
||||||
android:layout_width="24dp"
|
android:layout_width="24dp"
|
||||||
android:layout_height="24dp"
|
android:layout_height="24dp"
|
||||||
android:src="@drawable/baseline_location_pin_24"
|
android:src="@drawable/baseline_location_pin_24"
|
||||||
android:layout_gravity="center_vertical"
|
app:tint="@color/blue_300" />
|
||||||
app:tint="#3D84FF" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Alamat Pengiriman"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:fontFamily="@font/dmsans_medium"
|
|
||||||
android:layout_marginStart="8dp" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tv_places_address"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="-"
|
|
||||||
android:textColor="#5A5A5A"
|
|
||||||
android:paddingHorizontal="8dp"
|
|
||||||
android:paddingVertical="2dp"
|
|
||||||
android:textSize="12sp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginStart="32dp" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layout_marginTop="8dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tv_address"
|
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:text="-"
|
android:text="Alamat Pengiriman"
|
||||||
android:textSize="14sp"
|
android:textSize="16sp"
|
||||||
android:layout_marginStart="32dp" />
|
android:textColor="@android:color/black"
|
||||||
|
android:fontFamily="@font/dmsans_medium"
|
||||||
|
android:layout_marginStart="12dp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tv_change_address"
|
android:id="@+id/tv_change_address"
|
||||||
@ -104,169 +85,441 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Pilih Alamat"
|
android:text="Pilih Alamat"
|
||||||
android:textColor="#3D84FF"
|
android:textColor="#3D84FF"
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp"
|
||||||
|
android:fontFamily="@font/dmsans_medium"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:paddingHorizontal="8dp"
|
||||||
|
android:paddingVertical="4dp" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
<View
|
<!-- Empty Address State -->
|
||||||
android:layout_width="match_parent"
|
<LinearLayout
|
||||||
android:layout_height="8dp"
|
android:id="@+id/container_empty_address"
|
||||||
android:background="@color/black_50" />
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="8dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/address_header"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="Belum ada alamat dipilih"
|
||||||
|
android:fontFamily="@font/dmsans_medium"
|
||||||
|
android:textColor="#757575"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:gravity="center" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="Pilih alamat pengiriman untuk melanjutkan"
|
||||||
|
android:textColor="#BDBDBD"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:gravity="center" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Selected Address Content -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/container_address"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/address_header"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent">
|
||||||
|
|
||||||
|
<!-- Address Label -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_places_address"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Rumah"
|
||||||
|
android:textColor="#3D84FF"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:fontFamily="@font/dmsans_medium"
|
||||||
|
android:background="@drawable/bg_edit_text_background"
|
||||||
|
android:paddingHorizontal="8dp"
|
||||||
|
android:paddingVertical="4dp"
|
||||||
|
tools:text="Rumah" />
|
||||||
|
|
||||||
|
<!-- Full Address -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_address"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="Jl. Raya Serang No. 123, Kecamatan Serang, Kabupaten Serang, Banten 42111"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="@android:color/black"
|
||||||
|
android:lineSpacingExtra="2dp"
|
||||||
|
tools:text="Jl. Raya Serang No. 123, Kecamatan Serang, Kabupaten Serang, Banten 42111" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
|
||||||
<!-- Product Items Section -->
|
<!-- Product Items Section -->
|
||||||
<androidx.cardview.widget.CardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:id="@+id/card_product"
|
android:id="@+id/card_product"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:cardElevation="0dp">
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
app:cardBackgroundColor="@color/white"
|
||||||
|
app:strokeColor="#E0E0E0"
|
||||||
|
app:strokeWidth="1dp">
|
||||||
|
|
||||||
<LinearLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:padding="20dp">
|
||||||
android:background="@color/white"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<!-- Header Row -->
|
||||||
android:id="@+id/rv_product_items"
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:id="@+id/product_header"
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
tools:listitem="@layout/item_order_seller" />
|
|
||||||
</LinearLayout>
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="8dp"
|
|
||||||
android:background="#F5F5F5" />
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="8dp"
|
|
||||||
android:background="@color/black_50" />
|
|
||||||
|
|
||||||
<!-- Shipping Method Section -->
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/layout_shipping_method"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:background="@color/white"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:gravity="center_vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
|
||||||
android:text="Metode Pengiriman"
|
|
||||||
android:textSize="14sp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tv_shipping_option"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Opsi Pengiriman"
|
|
||||||
android:textColor="#3D84FF"
|
|
||||||
android:textSize="14sp" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:id="@+id/card_shipment"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:cardCornerRadius="8dp"
|
|
||||||
app:cardElevation="0dp"
|
|
||||||
app:cardBackgroundColor="#F5F5F5">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:padding="12dp">
|
android:gravity="center_vertical"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent">
|
||||||
|
|
||||||
<RadioButton
|
<ImageView
|
||||||
android:id="@+id/rb_jne"
|
android:layout_width="24dp"
|
||||||
android:layout_width="wrap_content"
|
android:layout_height="24dp"
|
||||||
android:layout_height="wrap_content"
|
android:src="@drawable/baseline_local_grocery_store_24"
|
||||||
android:checked="true" />
|
app:tint="@color/blue_300" />
|
||||||
|
|
||||||
<LinearLayout
|
<TextView
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:orientation="vertical"
|
android:text="Produk Pesanan"
|
||||||
android:layout_marginStart="8dp">
|
android:textSize="16sp"
|
||||||
|
android:textColor="@android:color/black"
|
||||||
|
android:fontFamily="@font/dmsans_medium"
|
||||||
|
android:layout_marginStart="12dp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
<!-- Empty Product State -->
|
||||||
android:id="@+id/tv_courier_name"
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:id="@+id/container_empty_products"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:text="JNE"
|
android:layout_height="wrap_content"
|
||||||
android:textSize="16sp"
|
android:orientation="vertical"
|
||||||
android:fontFamily="@font/dmsans_medium" />
|
android:layout_marginTop="4dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/product_header"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent">
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tv_delivery_estimate"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="3 - 4 hari kerja"
|
|
||||||
android:textSize="14sp"
|
|
||||||
android:textColor="#757575" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tv_shipping_price"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Rp15.000"
|
android:layout_marginTop="4dp"
|
||||||
android:textSize="16sp"
|
android:text="Tidak ada produk"
|
||||||
android:fontFamily="@font/dmsans_medium"
|
android:fontFamily="@font/dmsans_medium"
|
||||||
android:layout_gravity="center_vertical" />
|
android:textColor="#757575"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:gravity="center" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:text="Keranjang belanja kosong"
|
||||||
|
android:textColor="#BDBDBD"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:gravity="center" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<View
|
<!-- Products RecyclerView -->
|
||||||
android:layout_width="match_parent"
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:layout_height="8dp"
|
android:id="@+id/rv_product_items"
|
||||||
android:background="@color/black_50" />
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/product_header"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
tools:listitem="@layout/item_order_seller" />
|
||||||
|
|
||||||
<!-- Payment Method Section -->
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
<LinearLayout
|
</com.google.android.material.card.MaterialCardView>
|
||||||
android:id="@+id/layout_payment_method"
|
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/card_shipping_method"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:layout_marginHorizontal="16dp"
|
||||||
android:background="@color/white"
|
android:layout_marginTop="8dp"
|
||||||
android:padding="16dp">
|
app:cardBackgroundColor="@color/white"
|
||||||
|
app:strokeColor="#E0E0E0"
|
||||||
|
app:strokeWidth="1dp">
|
||||||
|
|
||||||
<TextView
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Metode Pembayaran"
|
android:padding="20dp">
|
||||||
android:textSize="14sp"
|
|
||||||
android:layout_marginBottom="8dp" />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<!-- Header Row -->
|
||||||
android:id="@+id/rv_payment_info"
|
<LinearLayout
|
||||||
|
android:id="@+id/shipping_header"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Metode Pengiriman"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@android:color/black"
|
||||||
|
android:fontFamily="@font/dmsans_medium"
|
||||||
|
android:layout_marginStart="12dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_shipping_option"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Pilih"
|
||||||
|
android:textColor="#3D84FF"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:fontFamily="@font/dmsans_medium"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:paddingHorizontal="8dp"
|
||||||
|
android:paddingVertical="4dp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Empty Shipping State -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/container_empty_shipping"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="12dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/shipping_header"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:text="Belum ada metode pengiriman"
|
||||||
|
android:fontFamily="@font/dmsans_medium"
|
||||||
|
android:textColor="#757575"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:gravity="center" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="Pilih alamat terlebih dahulu"
|
||||||
|
android:textColor="#BDBDBD"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:gravity="center" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Selected Shipping Content -->
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:id="@+id/card_shipment"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:cardCornerRadius="8dp"
|
||||||
|
app:cardElevation="0dp"
|
||||||
|
app:cardBackgroundColor="#F5F5F5"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/shipping_header"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="12dp">
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/rb_jne"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:checked="true" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_marginStart="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_courier_name"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="JNE"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:fontFamily="@font/dmsans_medium" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_delivery_estimate"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="3 - 4 hari kerja"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="#757575" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_shipping_price"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Rp15.000"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:fontFamily="@font/dmsans_medium"
|
||||||
|
android:layout_gravity="center_vertical" />
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Payment Method Section -->
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/card_payment_method"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
app:cardBackgroundColor="@color/white"
|
||||||
|
app:strokeColor="#E0E0E0"
|
||||||
|
app:strokeWidth="1dp">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
tools:listitem="@layout/item_payment_method" />
|
android:padding="20dp">
|
||||||
</LinearLayout>
|
|
||||||
|
<!-- Header Row -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/payment_header"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:src="@drawable/baseline_payment_24"
|
||||||
|
app:tint="@color/blue_300" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Metode Pembayaran"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@android:color/black"
|
||||||
|
android:fontFamily="@font/dmsans_medium"
|
||||||
|
android:layout_marginStart="12dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_payment_option"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Pilih"
|
||||||
|
android:textColor="#3D84FF"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:fontFamily="@font/dmsans_medium"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:paddingHorizontal="8dp"
|
||||||
|
android:paddingVertical="4dp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Empty Payment State -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/container_empty_payment"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="12dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/payment_header"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:text="Belum ada metode pembayaran"
|
||||||
|
android:fontFamily="@font/dmsans_medium"
|
||||||
|
android:textColor="#757575"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:gravity="center" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvEmptyPayment"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="Pilih alamat terlebih dahulu"
|
||||||
|
android:textColor="#BDBDBD"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:gravity="center" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Payment Methods RecyclerView -->
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rv_payment_info"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/payment_header"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
tools:listitem="@layout/item_payment_method" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="8dp"
|
android:layout_height="8dp"
|
||||||
|
android:layout_marginVertical="8dp"
|
||||||
android:background="@color/black_50" />
|
android:background="@color/black_50" />
|
||||||
|
|
||||||
<!-- Price Summary Section -->
|
<!-- Price Summary Section -->
|
||||||
@ -294,7 +547,7 @@
|
|||||||
android:id="@+id/tv_item_total"
|
android:id="@+id/tv_item_total"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Rp65.000"
|
android:text="Rp0"
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
@ -315,7 +568,7 @@
|
|||||||
android:id="@+id/tv_shipping_fee"
|
android:id="@+id/tv_shipping_fee"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Rp15.000"
|
android:text="Rp0"
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
@ -342,8 +595,8 @@
|
|||||||
android:id="@+id/tv_total"
|
android:id="@+id/tv_total"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Rp75.000"
|
android:text="Rp0"
|
||||||
android:textColor="#3D84FF"
|
android:textColor="@color/blue_400"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:fontFamily="@font/dmsans_bold" />
|
android:fontFamily="@font/dmsans_bold" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@ -379,7 +632,7 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Rp75.000"
|
android:text="Rp75.000"
|
||||||
android:textColor="#3D84FF"
|
android:textColor="@color/blue_400"
|
||||||
android:textSize="18sp"
|
android:textSize="18sp"
|
||||||
android:fontFamily="@font/dmsans_bold" />
|
android:fontFamily="@font/dmsans_bold" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@ -392,7 +645,7 @@
|
|||||||
android:textAllCaps="false"
|
android:textAllCaps="false"
|
||||||
android:paddingHorizontal="32dp"
|
android:paddingHorizontal="32dp"
|
||||||
app:cornerRadius="8dp"
|
app:cornerRadius="8dp"
|
||||||
android:backgroundTint="#3D84FF" />
|
android:backgroundTint="@color/blue_500" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@ -14,7 +14,6 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@ -167,7 +166,7 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textColor="@color/soft_gray"
|
android:textColor="@color/soft_gray"
|
||||||
android:textSize="14sp"
|
android:textSize="12sp"
|
||||||
tools:text="@string/item_sold" />
|
tools:text="@string/item_sold" />
|
||||||
|
|
||||||
<View
|
<View
|
||||||
@ -186,7 +185,7 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="4dp"
|
android:layout_marginStart="4dp"
|
||||||
android:textSize="14sp"
|
android:textSize="12sp"
|
||||||
tools:text="4.5" />
|
tools:text="4.5" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@ -219,7 +218,7 @@
|
|||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:text="@string/ulasan_pembeli"
|
android:text="@string/ulasan_pembeli"
|
||||||
android:textColor="@color/black"
|
android:textColor="@color/black"
|
||||||
android:textSize="16sp"
|
android:textSize="14sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -231,6 +230,18 @@
|
|||||||
android:textSize="14sp" />
|
android:textSize="14sp" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/empty_review"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Belum ada ulasan"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="@color/black_200"
|
||||||
|
android:gravity="center"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:fontFamily="@font/dmsans_mediumitalic"
|
||||||
|
android:layout_marginTop="8dp"/>
|
||||||
|
|
||||||
<!-- RecyclerView for Reviews -->
|
<!-- RecyclerView for Reviews -->
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/recyclerViewReviews"
|
android:id="@+id/recyclerViewReviews"
|
||||||
@ -265,7 +276,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/detail_produk"
|
android:text="@string/detail_produk"
|
||||||
android:textColor="@color/black"
|
android:textColor="@color/black"
|
||||||
android:textSize="16sp"
|
android:textSize="14sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
<TableLayout
|
<TableLayout
|
||||||
@ -284,7 +295,7 @@
|
|||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:text="@string/berat_produk"
|
android:text="@string/berat_produk"
|
||||||
android:fontFamily="@font/dmsans_semibold"
|
android:fontFamily="@font/dmsans_semibold"
|
||||||
android:textSize="14sp" />
|
android:textSize="12sp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvWeight"
|
android:id="@+id/tvWeight"
|
||||||
@ -292,7 +303,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:textColor="@color/blue_500"
|
android:textColor="@color/blue_500"
|
||||||
android:textSize="14sp"
|
android:textSize="12sp"
|
||||||
tools:text="200 gram" />
|
tools:text="200 gram" />
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
||||||
@ -307,7 +318,7 @@
|
|||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:text="@string/stock_product"
|
android:text="@string/stock_product"
|
||||||
android:fontFamily="@font/dmsans_semibold"
|
android:fontFamily="@font/dmsans_semibold"
|
||||||
android:textSize="14sp" />
|
android:textSize="12sp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvStock"
|
android:id="@+id/tvStock"
|
||||||
@ -315,7 +326,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:textColor="@color/blue_500"
|
android:textColor="@color/blue_500"
|
||||||
android:textSize="14sp"
|
android:textSize="12sp"
|
||||||
tools:text="100 buah" />
|
tools:text="100 buah" />
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
||||||
@ -346,8 +357,8 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/deskripsi_produk"
|
android:text="@string/deskripsi_produk"
|
||||||
android:textColor="@color/black"
|
android:textColor="@color/black"
|
||||||
android:textSize="16sp"
|
android:textSize="14sp"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="16dp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -356,7 +367,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:textColor="@color/black"
|
android:textColor="@color/black"
|
||||||
android:textSize="14sp"
|
android:textSize="12sp"
|
||||||
tools:text="Terbuat dari tepung dan ikan tenggiri asli Serang Banten. Tahan selama 25 hari." />
|
tools:text="Terbuat dari tepung dan ikan tenggiri asli Serang Banten. Tahan selama 25 hari." />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
@ -385,6 +396,13 @@
|
|||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:padding="16dp">
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progress_bar_detail_store"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_gravity="center"/>
|
||||||
|
|
||||||
<de.hdodenhof.circleimageview.CircleImageView
|
<de.hdodenhof.circleimageview.CircleImageView
|
||||||
android:id="@+id/ivSellerImage"
|
android:id="@+id/ivSellerImage"
|
||||||
android:layout_width="48dp"
|
android:layout_width="48dp"
|
||||||
@ -403,7 +421,7 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textColor="@color/black"
|
android:textColor="@color/black"
|
||||||
android:textSize="16sp"
|
android:textSize="14sp"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
tools:text="SnackEnak" />
|
tools:text="SnackEnak" />
|
||||||
|
|
||||||
@ -411,8 +429,9 @@
|
|||||||
android:id="@+id/tvSellerLocation"
|
android:id="@+id/tvSellerLocation"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:fontFamily="@font/dmsans_semibold"
|
android:fontFamily="@font/dmsans_regular"
|
||||||
android:textSize="14sp"
|
android:textColor="@color/black_300"
|
||||||
|
android:textSize="12sp"
|
||||||
tools:text="Jakarta Selatan" />
|
tools:text="Jakarta Selatan" />
|
||||||
|
|
||||||
<RatingBar
|
<RatingBar
|
||||||
@ -431,7 +450,7 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="2dp"
|
android:layout_marginTop="2dp"
|
||||||
android:textSize="14sp"
|
android:textSize="12sp"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
tools:text="5.0" />
|
tools:text="5.0" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@ -465,7 +484,7 @@
|
|||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:text="@string/produk_lainnya"
|
android:text="@string/produk_lainnya"
|
||||||
android:textColor="@color/black"
|
android:textColor="@color/black"
|
||||||
android:textSize="16sp"
|
android:textSize="14sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -477,6 +496,18 @@
|
|||||||
android:textSize="14sp" />
|
android:textSize="14sp" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/empty_other_products"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Belum ada produk lainnya"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="@color/black_200"
|
||||||
|
android:gravity="center"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:fontFamily="@font/dmsans_mediumitalic"
|
||||||
|
android:layout_marginTop="8dp"/>
|
||||||
|
|
||||||
<!-- RecyclerView for Other Products -->
|
<!-- RecyclerView for Other Products -->
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/recyclerViewOtherProducts"
|
android:id="@+id/recyclerViewOtherProducts"
|
||||||
@ -543,6 +574,7 @@
|
|||||||
android:insetBottom="0dp"
|
android:insetBottom="0dp"
|
||||||
android:text="@string/add_to_cart"
|
android:text="@string/add_to_cart"
|
||||||
android:textColor="@color/blue_500"
|
android:textColor="@color/blue_500"
|
||||||
|
android:textSize="14sp"
|
||||||
app:cornerRadius="4dp"
|
app:cornerRadius="4dp"
|
||||||
app:icon="@drawable/baseline_add_24"
|
app:icon="@drawable/baseline_add_24"
|
||||||
app:iconGravity="textStart"
|
app:iconGravity="textStart"
|
||||||
@ -560,6 +592,7 @@
|
|||||||
android:insetTop="0dp"
|
android:insetTop="0dp"
|
||||||
android:insetBottom="0dp"
|
android:insetBottom="0dp"
|
||||||
android:text="@string/beli_sekarang"
|
android:text="@string/beli_sekarang"
|
||||||
|
android:textSize="14sp"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
app:cornerRadius="4dp" />
|
app:cornerRadius="4dp" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@ -62,7 +62,7 @@
|
|||||||
android:id="@+id/profile_image"
|
android:id="@+id/profile_image"
|
||||||
android:layout_width="100dp"
|
android:layout_width="100dp"
|
||||||
android:layout_height="100dp"
|
android:layout_height="100dp"
|
||||||
android:src="@drawable/baseline_account_circle_24"
|
android:src="@drawable/baseline_account_circle_100"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"/>
|
app:layout_constraintEnd_toEndOf="parent"/>
|
||||||
@ -87,6 +87,7 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Ubah Profil"
|
android:text="Ubah Profil"
|
||||||
|
style="@style/button.large.active.medium"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
app:layout_constraintTop_toBottomOf="@id/profile_image"
|
app:layout_constraintTop_toBottomOf="@id/profile_image"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
|||||||
@ -61,7 +61,7 @@
|
|||||||
android:id="@+id/profile_image"
|
android:id="@+id/profile_image"
|
||||||
android:layout_width="100dp"
|
android:layout_width="100dp"
|
||||||
android:layout_height="100dp"
|
android:layout_height="100dp"
|
||||||
android:src="@drawable/baseline_account_circle_24"
|
android:src="@drawable/baseline_account_circle_100"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"/>
|
app:layout_constraintEnd_toEndOf="parent"/>
|
||||||
@ -188,6 +188,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Simpan"
|
android:text="Simpan"
|
||||||
|
style="@style/button.large.active.medium"
|
||||||
android:layout_marginTop="32dp"
|
android:layout_marginTop="32dp"
|
||||||
android:layout_marginHorizontal="16dp"
|
android:layout_marginHorizontal="16dp"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
|
|||||||
@ -6,6 +6,8 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:paddingHorizontal="32dp"
|
android:paddingHorizontal="32dp"
|
||||||
android:paddingVertical="16dp"
|
android:paddingVertical="16dp"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
android:theme="@style/Theme.Ecommerce_serang"
|
||||||
tools:context=".ui.auth.LoginActivity">
|
tools:context=".ui.auth.LoginActivity">
|
||||||
|
|
||||||
<!-- Title -->
|
<!-- Title -->
|
||||||
@ -136,6 +138,7 @@
|
|||||||
android:id="@+id/tv_registrasi"
|
android:id="@+id/tv_registrasi"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
android:text="@string/signup"
|
android:text="@string/signup"
|
||||||
android:textColor="@color/blue1"
|
android:textColor="@color/blue1"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|||||||
@ -69,6 +69,35 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/layout_rejected"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center"
|
||||||
|
android:background="@drawable/bg_product_active"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/label_small"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:text="Permintaan Buka Toko Anda sebelumnya ditolak! Silahkan lakukan penyesuaian berdasarkan alasan berikut:"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_rejected_reason"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/body_medium"
|
||||||
|
android:fontFamily="@font/dmsans_bold"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="KTP tidak sesuai"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- Nama Toko -->
|
<!-- Nama Toko -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@ -15,156 +15,207 @@
|
|||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<!-- Store Information -->
|
<!-- Store Information Card -->
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:id="@+id/storeInfoContainer"
|
android:id="@+id/storeInfoCard"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@color/blue_50"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="20dp"
|
||||||
android:padding="24dp"
|
android:layout_marginEnd="16dp"
|
||||||
|
app:cardBackgroundColor="@android:color/white"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
app:cardElevation="4dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/searchContainer">
|
app:layout_constraintTop_toBottomOf="@id/searchContainer">
|
||||||
|
|
||||||
<ImageView
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/ivStoreImage"
|
android:id="@+id/storeInfoContainer"
|
||||||
android:layout_width="64dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="64dp"
|
|
||||||
android:background="@drawable/circle_background"
|
|
||||||
android:scaleType="centerCrop"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:src="@drawable/placeholder_image" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tvStoreName"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:padding="24dp">
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:textColor="@android:color/black"
|
|
||||||
android:textSize="18sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/ivStoreImage"
|
|
||||||
app:layout_constraintTop_toTopOf="@id/ivStoreImage"
|
|
||||||
tools:text="SnackEnak" />
|
|
||||||
|
|
||||||
<TextView
|
<!-- Store Image with Material Card wrapper -->
|
||||||
android:id="@+id/tvStoreType"
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:layout_width="0dp"
|
android:id="@+id/storeImageCard"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="80dp"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_height="80dp"
|
||||||
android:layout_marginEnd="16dp"
|
app:cardCornerRadius="16dp"
|
||||||
android:textSize="14sp"
|
app:cardElevation="2dp"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@id/ivStoreImage"
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
app:layout_constraintTop_toBottomOf="@id/tvStoreName"
|
|
||||||
tools:text="Makanan Ringan" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<ImageView
|
||||||
android:id="@+id/storeRatingContainer"
|
android:id="@+id/ivStoreImage"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginStart="16dp"
|
android:scaleType="centerCrop"
|
||||||
android:layout_marginTop="4dp"
|
tools:src="@drawable/placeholder_image" />
|
||||||
android:gravity="center_vertical"
|
</com.google.android.material.card.MaterialCardView>
|
||||||
android:orientation="horizontal"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/ivStoreImage"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/tvStoreType">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/ivStoreRatingStar"
|
|
||||||
android:layout_width="16dp"
|
|
||||||
android:layout_height="16dp"
|
|
||||||
android:src="@drawable/ic_star"
|
|
||||||
app:tint="@color/yellow" />
|
|
||||||
|
|
||||||
|
<!-- Store Name -->
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvStoreRating"
|
android:id="@+id/tvStoreName"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="4dp"
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:textColor="@android:color/black"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/storeImageCard"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/storeImageCard"
|
||||||
|
tools:text="SnackEnak Store" />
|
||||||
|
|
||||||
|
<!-- Store Type -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvStoreType"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:background="@drawable/search_background"
|
||||||
|
android:paddingStart="12dp"
|
||||||
|
android:paddingTop="4dp"
|
||||||
|
android:paddingEnd="12dp"
|
||||||
|
android:paddingBottom="4dp"
|
||||||
|
android:textColor="@color/blue_500"
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
tools:text="5.0" />
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
</LinearLayout>
|
app:layout_constraintStart_toEndOf="@id/storeImageCard"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/tvStoreName"
|
||||||
|
tools:text="Makanan Ringan" />
|
||||||
|
|
||||||
<TextView
|
<!-- Rating Container -->
|
||||||
android:id="@+id/tvStoreLocation"
|
<LinearLayout
|
||||||
android:layout_width="0dp"
|
android:id="@+id/storeRatingContainer"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="4dp"
|
android:layout_marginStart="20dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginTop="8dp"
|
||||||
android:textSize="12sp"
|
android:gravity="center_vertical"
|
||||||
app:layout_constraintEnd_toStartOf="@id/tvActiveStatus"
|
android:orientation="horizontal"
|
||||||
app:layout_constraintStart_toEndOf="@id/ivStoreImage"
|
app:layout_constraintStart_toEndOf="@id/storeImageCard"
|
||||||
app:layout_constraintTop_toBottomOf="@id/storeRatingContainer"
|
app:layout_constraintTop_toBottomOf="@id/tvStoreType">
|
||||||
tools:text="Kabupaten Serang" />
|
|
||||||
|
|
||||||
<TextView
|
<ImageView
|
||||||
android:id="@+id/tvActiveStatus"
|
android:id="@+id/ivStoreRatingStar"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="16dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="16dp"
|
||||||
android:paddingStart="4dp"
|
android:src="@drawable/ic_star"
|
||||||
android:paddingEnd="4dp"
|
app:tint="@color/yellow" />
|
||||||
android:text="Aktif"
|
|
||||||
android:textColor="@android:color/black"
|
|
||||||
android:textSize="12sp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@id/tvStoreLocation"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="@id/tvStoreLocation" />
|
|
||||||
|
|
||||||
<!-- <ImageButton-->
|
<TextView
|
||||||
<!-- android:id="@+id/btnChevron"-->
|
android:id="@+id/tvStoreRating"
|
||||||
<!-- android:layout_width="wrap_content"-->
|
android:layout_width="wrap_content"
|
||||||
<!-- android:layout_height="wrap_content"-->
|
android:layout_height="wrap_content"
|
||||||
<!-- android:background="?attr/selectableItemBackgroundBorderless"-->
|
android:layout_marginStart="4dp"
|
||||||
<!-- android:contentDescription="More"-->
|
android:textColor="@android:color/black"
|
||||||
<!-- android:src="@drawable/ic_chevron_right"-->
|
android:textSize="14sp"
|
||||||
<!-- app:layout_constraintBottom_toBottomOf="parent"-->
|
android:textStyle="bold"
|
||||||
<!-- app:layout_constraintEnd_toEndOf="parent"-->
|
tools:text="4.8" />
|
||||||
<!-- app:layout_constraintTop_toTopOf="parent" />-->
|
</LinearLayout>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
<!-- Location and Status Row -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/locationStatusContainer"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/storeImageCard"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/storeRatingContainer">
|
||||||
|
|
||||||
|
<!-- Location with Icon -->
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="14dp"
|
||||||
|
android:layout_height="14dp"
|
||||||
|
android:src="@drawable/ic_location"
|
||||||
|
app:tint="@color/black_300" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvStoreLocation"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="@color/blue_400"
|
||||||
|
android:textSize="13sp"
|
||||||
|
tools:text="Kabupaten Serang" />
|
||||||
|
|
||||||
|
<!-- Status Indicator -->
|
||||||
|
<View
|
||||||
|
android:id="@+id/statusDot"
|
||||||
|
android:layout_width="6dp"
|
||||||
|
android:layout_height="6dp"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:layout_marginEnd="6dp"
|
||||||
|
android:background="@drawable/baseline_circle_24"
|
||||||
|
android:backgroundTint="@color/black_300"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvActiveStatus"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/blue_500"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:fontFamily="@font/dmsans_semibold"
|
||||||
|
tools:text="Aktif" />
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<!-- Divider -->
|
||||||
<com.google.android.material.divider.MaterialDivider
|
<com.google.android.material.divider.MaterialDivider
|
||||||
android:id="@+id/divider_product"
|
android:id="@+id/divider_product"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintTop_toBottomOf="@id/storeInfoContainer"/>
|
android:layout_marginTop="24dp"
|
||||||
|
app:dividerColor="@color/black_200"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/storeInfoCard" />
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
<!-- Tab Layout: TO DO implement after review -->
|
android:id="@+id/progress_bar_detail_prod_item"
|
||||||
<!-- <com.google.android.material.tabs.TabLayout-->
|
android:layout_width="wrap_content"
|
||||||
<!-- android:id="@+id/tabLayout"-->
|
android:layout_height="wrap_content"
|
||||||
<!-- android:layout_width="match_parent"-->
|
android:visibility="gone"
|
||||||
<!-- android:layout_height="wrap_content"-->
|
android:layout_gravity="center"
|
||||||
<!-- app:layout_constraintTop_toBottomOf="@id/storeInfoContainer"-->
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
<!-- app:tabIndicatorColor="@color/colorPrimary"-->
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
<!-- app:tabSelectedTextColor="@color/colorPrimary"-->
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
<!-- app:tabTextColor="@android:color/darker_gray">-->
|
app:layout_constraintTop_toBottomOf="@id/divider_product"/>
|
||||||
|
|
||||||
<!-- <com.google.android.material.tabs.TabItem-->
|
|
||||||
<!-- android:layout_width="wrap_content"-->
|
|
||||||
<!-- android:layout_height="wrap_content"-->
|
|
||||||
<!-- android:text="Produk" />-->
|
|
||||||
|
|
||||||
<!-- <com.google.android.material.tabs.TabItem-->
|
|
||||||
<!-- android:layout_width="wrap_content"-->
|
|
||||||
<!-- android:layout_height="wrap_content"-->
|
|
||||||
<!-- android:text="Kategori" />-->
|
|
||||||
<!-- </com.google.android.material.tabs.TabLayout>-->
|
|
||||||
|
|
||||||
<!-- Products RecyclerView -->
|
<!-- Products RecyclerView -->
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/rv_products"
|
android:id="@+id/rv_products"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingBottom="16dp"
|
||||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/divider_product"
|
app:layout_constraintTop_toBottomOf="@id/divider_product"
|
||||||
app:spanCount="2"
|
app:spanCount="2"
|
||||||
tools:listitem="@layout/item_product_grid" />
|
tools:listitem="@layout/item_product_grid" />
|
||||||
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@ -168,8 +168,13 @@
|
|||||||
app:layout_constraintTop_toBottomOf="@id/searchContainer" />
|
app:layout_constraintTop_toBottomOf="@id/searchContainer" />
|
||||||
|
|
||||||
<include
|
<include
|
||||||
android:id="@+id/loading"
|
android:id="@+id/loadingAll"
|
||||||
layout="@layout/view_loading"/>
|
layout="@layout/view_loading"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"/>
|
||||||
|
|
||||||
<include
|
<include
|
||||||
android:id="@+id/error"
|
android:id="@+id/error"
|
||||||
|
|||||||
@ -304,6 +304,55 @@
|
|||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<!-- Change Password Card -->
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:id="@+id/card_change_pass"
|
||||||
|
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/ivChangePass"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:src="@drawable/ic_change_pass"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvChangePass"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:text="Ubah Kata Sandi"
|
||||||
|
android:textSize="14sp"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/ivChangePass"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/ivChangePassArrow" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ivChangePassArrow"
|
||||||
|
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 -->
|
<!-- About Card -->
|
||||||
<androidx.cardview.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
android:id="@+id/card_about"
|
android:id="@+id/card_about"
|
||||||
|
|||||||
@ -50,8 +50,9 @@
|
|||||||
android:id="@+id/et_otp"
|
android:id="@+id/et_otp"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:hint="Enter OTP"
|
android:hint="Masukkan OTP"
|
||||||
android:inputType="number"
|
android:inputType="number"
|
||||||
|
android:textSize="16dp"
|
||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
android:maxLength="6" />
|
android:maxLength="6" />
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
@ -61,9 +62,19 @@
|
|||||||
android:id="@+id/btn_verify"
|
android:id="@+id/btn_verify"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Verify"
|
android:text="Kirim kode OTP"
|
||||||
|
style="@style/button.large.active.medium"
|
||||||
app:cornerRadius="8dp" />
|
app:cornerRadius="8dp" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btn_back"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
style="@style/Widget.Material3.Button.OutlinedButton.Icon"
|
||||||
|
android:text="Kembali"
|
||||||
|
app:cornerRadius="8dp"/>
|
||||||
|
|
||||||
<!-- Resend OTP -->
|
<!-- Resend OTP -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -81,7 +92,7 @@
|
|||||||
android:id="@+id/tv_resend_otp"
|
android:id="@+id/tv_resend_otp"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Resend"
|
android:text="Kirim Ulang"
|
||||||
android:textColor="@color/blue1"
|
android:textColor="@color/blue1"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@ -233,6 +233,12 @@
|
|||||||
android:padding="12dp"
|
android:padding="12dp"
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/checkbox_approve"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Saya telah membaca dan menyetujui Syarat dan Ketentuan aplikasi" />
|
||||||
|
|
||||||
<!-- Navigation Button (Previous) -->
|
<!-- Navigation Button (Previous) -->
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/btn_previous"
|
android:id="@+id/btn_previous"
|
||||||
@ -240,24 +246,14 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="24dp"
|
android:layout_marginTop="24dp"
|
||||||
android:background="@drawable/bg_button_outline"
|
android:background="@drawable/bg_button_outline"
|
||||||
|
android:visibility="gone"
|
||||||
android:text="Kembali"
|
android:text="Kembali"
|
||||||
android:textAllCaps="false"
|
android:textAllCaps="false"
|
||||||
android:textColor="@color/blue1"
|
android:textColor="@color/blue1"
|
||||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"/>
|
style="@style/Widget.Material3.Button.OutlinedButton.Icon"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<CheckBox
|
|
||||||
android:id="@+id/checkbox_approve"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Saya telah membaca dan menyetujui Syarat dan Ketentuan aplikasi" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
<!-- Register Button -->
|
<!-- Register Button -->
|
||||||
@ -265,7 +261,7 @@
|
|||||||
android:id="@+id/btn_register"
|
android:id="@+id/btn_register"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="56dp"
|
android:layout_height="56dp"
|
||||||
android:layout_margin="16dp"
|
android:layout_marginHorizontal="16dp"
|
||||||
android:background="@drawable/button_address_background"
|
android:background="@drawable/button_address_background"
|
||||||
android:text="@string/signup"
|
android:text="@string/signup"
|
||||||
android:textAllCaps="false"
|
android:textAllCaps="false"
|
||||||
|
|||||||
@ -9,10 +9,9 @@
|
|||||||
android:id="@+id/content"
|
android:id="@+id/content"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:gravity="start"
|
android:gravity="center"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginVertical="8dp"
|
android:layout_marginVertical="8dp"
|
||||||
android:layout_marginBottom="4dp"
|
|
||||||
android:layout_gravity="center_horizontal"
|
android:layout_gravity="center_horizontal"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
<RadioButton
|
<RadioButton
|
||||||
@ -24,38 +23,53 @@
|
|||||||
android:layout_height="wrap_content"/>
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/courier_name_cost"
|
|
||||||
android:fontFamily="@font/dmsans_semibold"
|
|
||||||
android:textSize="20sp"
|
|
||||||
android:padding="4dp"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="JNE"/>
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/est_date"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:paddingHorizontal="8dp"
|
|
||||||
android:text="Estimasi 3-4 hari"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/cost_price"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:layout_margin="16dp"
|
|
||||||
android:fontFamily="@font/dmsans_semibold"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:text="Rp15.0000"/>
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:weightSum="1">
|
||||||
|
|
||||||
|
<!-- Left Section -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="0.7"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/courier_name_cost"
|
||||||
|
android:fontFamily="@font/dmsans_semibold"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:paddingHorizontal="2dp"
|
||||||
|
android:paddingTop="4dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:scrollHorizontally="false"
|
||||||
|
android:singleLine="false"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:text="JNE Express"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/est_date"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:paddingHorizontal="4dp"
|
||||||
|
android:text="Estimasi 3-4 hari"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Right Section -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/cost_price"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="0.3"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:gravity="start"
|
||||||
|
android:fontFamily="@font/dmsans_semibold"
|
||||||
|
android:text="Rp15.000"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@ -22,7 +22,8 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:text="Retry"
|
android:text="Muat Ulang"
|
||||||
|
style="@style/Widget.Material3.FloatingActionButton.Large.Surface"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
|||||||
@ -6,12 +6,13 @@
|
|||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/progressBar"
|
android:id="@+id/progressBar"
|
||||||
android:layout_width="24dp"
|
android:layout_width="64dp"
|
||||||
android:layout_height="24dp"
|
android:layout_height="64dp"
|
||||||
android:layout_gravity="center_horizontal"
|
android:layout_gravity="center_horizontal"
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"/>
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"/>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@ -147,29 +147,112 @@
|
|||||||
<string name="buka_toko_desc">Mohon untuk melengkapi formulir pendaftaran ini agar dapat mengakses fitur penjual pada aplikasi.</string>
|
<string name="buka_toko_desc">Mohon untuk melengkapi formulir pendaftaran ini agar dapat mengakses fitur penjual pada aplikasi.</string>
|
||||||
|
|
||||||
<!-- List Bank -->
|
<!-- List Bank -->
|
||||||
<string-array name="bank_name_array">
|
<string-array name="bank_names">
|
||||||
|
<item>Bank of America</item>
|
||||||
|
<item>Bank of China (Hong Kong)</item>
|
||||||
|
<item>Citibank</item>
|
||||||
|
<item>Deutsche Bank Ag</item>
|
||||||
|
<item>JP Morgan Chase Bank</item>
|
||||||
|
<item>MUFG Bank</item>
|
||||||
<item>Allo Bank Indonesia</item>
|
<item>Allo Bank Indonesia</item>
|
||||||
<item>Bank Digital BCA</item>
|
<item>Bank Aceh Syariah</item>
|
||||||
<item>Bank Central Asia (BCA)</item>
|
<item>Bank Aladin Syariah</item>
|
||||||
|
<item>Bank Amar Indonesia</item>
|
||||||
|
<item>Bank ANZ Indonesia</item>
|
||||||
|
<item>Bank Artha Graha Internasional</item>
|
||||||
<item>Bank BCA Syariah</item>
|
<item>Bank BCA Syariah</item>
|
||||||
<item>Bank Jago</item>
|
<item>Bank BNP Paribas Indonesia</item>
|
||||||
<item>Bank Mandiri </item>
|
<item>Bank BTPN Syariah</item>
|
||||||
<item>Bank Kb Bukopi</item>
|
<item>Bank Bumi Arta</item>
|
||||||
|
<item>Bank Capital Indonesia</item>
|
||||||
|
<item>Bank Central Asia (BCA)</item>
|
||||||
|
<item>Bank China Construction Bank Indonesia</item>
|
||||||
|
<item>Bank CIMB Niaga</item>
|
||||||
|
<item>Bank CTBC Indonesia</item>
|
||||||
|
<item>Bank Danamon Indonesia</item>
|
||||||
|
<item>Bank DBS Indonesia</item>
|
||||||
|
<item>Bank Digital BCA</item>
|
||||||
|
<item>Bank Ganesha</item>
|
||||||
|
<item>Bank Hibank Indonesia</item>
|
||||||
|
<item>Bank HSBC Indonesia</item>
|
||||||
|
<item>Bank IBK Indonesia</item>
|
||||||
|
<item>Bank ICBC Indonesia</item>
|
||||||
|
<item>Bank Ina Perdana</item>
|
||||||
|
<item>Bank Index Selindo</item>
|
||||||
<item>Bank Jabar Banten Syariah</item>
|
<item>Bank Jabar Banten Syariah</item>
|
||||||
<item> Bank Mandiri Taspen</item>
|
<item>Bank Jago</item>
|
||||||
<item>Bank Maybank Indonesia </item>
|
<item>Bank JTrust Indonesia</item>
|
||||||
|
<item>Bank Kb Bukopin Syariah</item>
|
||||||
|
<item>Bank Kb Bukopin</item>
|
||||||
|
<item>Bank Keb Hana Indonesia</item>
|
||||||
|
<item>Bank Mandiri</item>
|
||||||
|
<item>Bank Mandiri Taspen</item>
|
||||||
|
<item>Bank Maspion Indonesia</item>
|
||||||
|
<item>Bank Mayapada International</item>
|
||||||
|
<item>Bank Maybank Indonesia</item>
|
||||||
|
<item>Bank Mega Syariah</item>
|
||||||
|
<item>Bank Mega</item>
|
||||||
|
<item>Bank Mestika Dharma</item>
|
||||||
|
<item>Bank Mizuho Indonesia</item>
|
||||||
|
<item>Bank MNC Internasional</item>
|
||||||
|
<item>Bank Muamalat Indonesia</item>
|
||||||
|
<item>Bank Multiarta Sentosa</item>
|
||||||
|
<item>Bank Nagari</item>
|
||||||
|
<item>Bank Nano Syariah</item>
|
||||||
|
<item>Bank Nationalnobu</item>
|
||||||
<item>Bank Negara Indonesia (BNI)</item>
|
<item>Bank Negara Indonesia (BNI)</item>
|
||||||
|
<item>Bank Neo Commerce</item>
|
||||||
|
<item>Bank NTB Syariah</item>
|
||||||
|
<item>Bank OCBC NISP</item>
|
||||||
|
<item>Bank of India Indonesia</item>
|
||||||
|
<item>Bank Oke Indonesia</item>
|
||||||
|
<item>Bank PAN Indonesia</item>
|
||||||
|
<item>Bank Panin Dubai Syariah</item>
|
||||||
|
<item>Bank Pembangunan Daerah (BPD) Bali</item>
|
||||||
<item>Bank Permata</item>
|
<item>Bank Permata</item>
|
||||||
|
<item>Bank QNB Indonesia</item>
|
||||||
<item>Bank Rakyat Indonesia (BRI)</item>
|
<item>Bank Rakyat Indonesia (BRI)</item>
|
||||||
|
<item>Bank Raya Indonesia</item>
|
||||||
|
<item>Bank Resona Perdania</item>
|
||||||
|
<item>Bank Sahabat Sampoerna</item>
|
||||||
<item>Bank Saqu Indonesia</item>
|
<item>Bank Saqu Indonesia</item>
|
||||||
|
<item>Bank SBI Indonesia</item>
|
||||||
<item>Bank Seabank Indonesia</item>
|
<item>Bank Seabank Indonesia</item>
|
||||||
<item>Bank Sinarmas </item>
|
<item>Bank Shinhan Indonesia</item>
|
||||||
|
<item>Bank Sinarmas</item>
|
||||||
|
<item>Bank SMBC Indonesia</item>
|
||||||
<item>Bank Syariah Indonesia (BSI)</item>
|
<item>Bank Syariah Indonesia (BSI)</item>
|
||||||
<item>Bank Tabungan Negara (BTN)</item>
|
<item>Bank Tabungan Negara (BTN)</item>
|
||||||
<item>Bank UOB Indonesia</item>
|
<item>Bank UOB Indonesia</item>
|
||||||
|
<item>Bank Victoria International</item>
|
||||||
|
<item>Bank Victoria Syariah</item>
|
||||||
|
<item>Bank Woori Saudara Indonesia 1906</item>
|
||||||
|
<item>BPD Banten</item>
|
||||||
|
<item>BPD Bengkulu</item>
|
||||||
|
<item>BPD Daerah Istimewa Yogyakarta</item>
|
||||||
|
<item>BPD DKI</item>
|
||||||
|
<item>BPD Jambi</item>
|
||||||
<item>BPD Jawa Barat dan Banten</item>
|
<item>BPD Jawa Barat dan Banten</item>
|
||||||
|
<item>BPD Jawa Tengah</item>
|
||||||
|
<item>BPD Jawa Timur</item>
|
||||||
|
<item>BPD Kalimantan Barat</item>
|
||||||
|
<item>BPD Kalimantan Selatan</item>
|
||||||
|
<item>BPD Kalimantan Tengah</item>
|
||||||
|
<item>BPD Kalimantan Timur dan Kalimantan Utara</item>
|
||||||
|
<item>BPD Lampung</item>
|
||||||
|
<item>BPD Maluku dan Maluku Utara</item>
|
||||||
|
<item>BPD Nusa Tenggara Timur</item>
|
||||||
|
<item>BPD Papua</item>
|
||||||
|
<item>BPD Riau Kepri Syariah</item>
|
||||||
|
<item>BPD Sulawesi Selatan dan Sulawesi Barat</item>
|
||||||
|
<item>BPD Sulawesi Tengah</item>
|
||||||
|
<item>BPD Sulawesi Tenggara</item>
|
||||||
|
<item>BPD Sulawesi Utara dan Gorontalo</item>
|
||||||
|
<item>BPD Sumatera Selatan dan Bangka Belitung</item>
|
||||||
|
<item>BPD Sumatera Utara</item>
|
||||||
|
<item>Krom Bank Indonesia</item>
|
||||||
<item>Super Bank Indonesia</item>
|
<item>Super Bank Indonesia</item>
|
||||||
|
<item>Standard Chartered Bank</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
@ -1,4 +1,4 @@
|
|||||||
<resources>
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<!-- Base application theme. -->
|
<!-- Base application theme. -->
|
||||||
<style name="Theme.Ecommerce_serang" parent="Theme.Material3.Light.NoActionBar">
|
<style name="Theme.Ecommerce_serang" parent="Theme.Material3.Light.NoActionBar">
|
||||||
<!-- Primary Color Customization -->
|
<!-- Primary Color Customization -->
|
||||||
@ -269,7 +269,7 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="button.small.active.short">
|
<style name="button.small.active.short">
|
||||||
<item name="android:layout_width">144dp</item>
|
<item name="android:layout_width">100dp</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="button.small.active.short.only_icon">
|
<style name="button.small.active.short.only_icon">
|
||||||
@ -330,4 +330,12 @@
|
|||||||
<item name="cornerSize">5dp</item>
|
<item name="cornerSize">5dp</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
</resources>
|
|
||||||
|
<style name="Theme.App.SplashScreen" parent="Theme.SplashScreen">
|
||||||
|
<item name="windowSplashScreenBackground">@color/white</item>
|
||||||
|
<item name="windowSplashScreenAnimatedIcon">@drawable/logo_bisa_umkm</item>
|
||||||
|
<item name="windowSplashScreenAnimationDuration">1000</item>
|
||||||
|
<item name="android:windowSplashScreenBehavior" tools:targetApi="33">icon_preferred</item>
|
||||||
|
<item name="postSplashScreenTheme">@style/Theme.Ecommerce_serang</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
BIN
unduh/pasarKlik_v1.apk
Normal file
BIN
unduh/pasarKlik_v1.apk
Normal file
Binary file not shown.
Reference in New Issue
Block a user