Compare commits

1 Commits

Author SHA1 Message Date
4d24673107 Add files via upload 2025-08-21 13:39:03 +07:00
64 changed files with 660 additions and 1748 deletions

View File

@ -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-08-17T17:32:55.497700100Z"> <DropdownSelection timestamp="2025-05-08T14:50:55.425322500Z">
<Target type="DEFAULT_BOOT"> <Target type="DEFAULT_BOOT">
<handle> <handle>
<DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\Gracia Hotmauli\.android\avd\Pixel_9_2.avd" /> <DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\Gracia Hotmauli\.android\avd\Pixel_8_2_2.avd" />
</handle> </handle>
</Target> </Target>
</DropdownSelection> </DropdownSelection>

View File

@ -124,7 +124,4 @@ 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")
} }

View File

@ -29,9 +29,6 @@
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" />

View File

@ -10,6 +10,9 @@ 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,
@ -34,6 +37,9 @@ 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,

View File

@ -1,13 +1,18 @@
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
) )

View File

@ -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.store.StoreResponse import com.alya.ecommerce_serang.data.api.response.customer.product.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<StoreResponse?> { suspend fun fetchMyStoreProfile(): Result<Store?> {
return try { return try {
val response = apiService.getMyStoreData() val response = apiService.getStore()
if (response.isSuccessful) { if (response.isSuccessful) {
val storeResponse = response.body() val storeResponse: StoreResponse? = response.body()
Result.Success(storeResponse) Result.Success(storeResponse?.store)
} 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")

View File

@ -72,7 +72,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, "Mohon masukkan email atau password dengan benar", Toast.LENGTH_SHORT).show() Toast.makeText(this, "Please fill in all fields", Toast.LENGTH_SHORT).show()
} else { } else {
loginViewModel.login(email, password) loginViewModel.login(email, password)
} }
@ -100,14 +100,14 @@ class LoginActivity : AppCompatActivity() {
retrieveFCMToken() retrieveFCMToken()
// sessionManager.saveUserId(response.userId) // sessionManager.saveUserId(response.userId)
Toast.makeText(this, "Berhasil masuk", Toast.LENGTH_SHORT).show() Toast.makeText(this, "Login Successful", 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, "Gagal masuk", Toast.LENGTH_LONG).show() Toast.makeText(this, "Login Failed: ${result.exception.message}", Toast.LENGTH_LONG).show()
} }
is Result.Loading -> { is Result.Loading -> {
// Show loading state // Show loading state

View File

@ -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(), "Silahkan masukkan kode OTP", Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), "Please enter OTP", Toast.LENGTH_SHORT).show()
} }
} }
return view return view

View File

@ -86,72 +86,20 @@ class RegisterActivity : AppCompatActivity() {
} }
} }
// In RegisterActivity, add debug to navigateToStep: // navigate step register in fragment
fun navigateToStep(step: Int, userData: RegisterRequest?) { fun navigateToStep(step: Int, userData: RegisterRequest?) {
Log.d("RegisterActivity", "=== NAVIGATE TO STEP START ===") val fragment = when (step) {
Log.d("RegisterActivity", "Target step: $step") 1 -> RegisterStep1Fragment.newInstance()
Log.d("RegisterActivity", "Current fragment count: ${supportFragmentManager.fragments.size}") 2 -> RegisterStep2Fragment.newInstance(userData)
Log.d("RegisterActivity", "UserData: ${userData?.email}") 3 -> RegisterStep3Fragment.newInstance()
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 {
try { supportFragmentManager.beginTransaction()
val fragment = when (step) { .replace(R.id.fragment_container, it)
1 -> { .addToBackStack(null)
Log.d("RegisterActivity", "Creating RegisterStep1Fragment") .commit()
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)
} }
} }
} }

View File

@ -111,7 +111,7 @@ class ResetPassActivity : AppCompatActivity() {
} }
private fun handleError(errorMessage: String) { private fun handleError(errorMessage: String) {
Log.e(TAG, "Error: $errorMessage") Toast.makeText(this, "Error: $errorMessage", Toast.LENGTH_LONG).show()
// Optionally show error dialog // Optionally show error dialog
AlertDialog.Builder(this) AlertDialog.Builder(this)

View File

@ -155,20 +155,19 @@ class RegisterStep1Fragment : Fragment() {
"email" -> { "email" -> {
isEmailValid = isValid isEmailValid = isValid
if (!isValid) { if (!isValid) {
Toast.makeText(requireContext(), "Email sudah digunakan. Gunakan email lainnya.", Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), "Email is already registered", Toast.LENGTH_SHORT).show()
} }
} }
"phone" -> { "phone" -> {
isPhoneValid = isValid isPhoneValid = isValid
if (!isValid) { if (!isValid) {
Toast.makeText(requireContext(), "Nomor handphone sudah digunakan. Gunakan nomor lainnya. ", Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), "Phone number is already registered", 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(), "Gagal melakukan validasi", Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), "Validation failed: ${result.exception.message}", Toast.LENGTH_SHORT).show()
Log.e(TAG, "Validation failed: ${result.exception.message}")
} }
} }
} }
@ -201,8 +200,7 @@ 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
Log.e(TAG, "OTP Request Failed: ${result.exception.message}") Toast.makeText(requireContext(), "OTP Request Failed: ${result.exception.message}", Toast.LENGTH_SHORT).show()
Toast.makeText(requireContext(), "Gagal mendapatkan OTP. Kirim ulang OTP", Toast.LENGTH_SHORT).show()
} }
} }
} }
@ -231,13 +229,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(), "Silahkan lengkapi seluruh isian", Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), "Please fill all required fields", Toast.LENGTH_SHORT).show()
return return
} }
// Check if passwords match // Check if passwords match
if (password != confirmPassword) { if (password != confirmPassword) {
Toast.makeText(requireContext(), "Konfirmasi kata sandi tidak sesua. Periksa kembali", Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), "Passwords do not match", Toast.LENGTH_SHORT).show()
return return
} }
@ -255,7 +253,7 @@ class RegisterStep1Fragment : Fragment() {
if (isEmailValid && isPhoneValid) { if (isEmailValid && isPhoneValid) {
requestOtp(email) requestOtp(email)
} else { } else {
Toast.makeText(requireContext(), "Silahkan perbaiki data yang dimasukkan", Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), "Please fix validation errors before proceeding", Toast.LENGTH_SHORT).show()
} }
} }

View File

@ -1,7 +1,5 @@
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
@ -15,7 +13,6 @@ 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
@ -27,15 +24,11 @@ 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 {
@ -46,8 +39,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 {
@ -119,20 +112,6 @@ 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?) {
@ -150,6 +129,11 @@ 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")
} }
@ -186,9 +170,37 @@ 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) {
@ -238,8 +250,6 @@ 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
@ -269,148 +279,9 @@ class RegisterStep2Fragment : Fragment() {
} }
} }
private fun retrieveFCMToken() {
FirebaseMessaging.getInstance().token
.addOnCompleteListener { task ->
if (!task.isSuccessful) {
Log.e(TAG, "Failed to get FCM token", task.exception)
return@addOnCompleteListener
}
val token = task.result
// tokenTes = token
Log.d(TAG, "FCM token retrieved: $token")
// Save token locally
val sharedPreferences = 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 = 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() { override fun onDestroyView() {
Log.d(TAG, "onDestroyView - cleaning up")
super.onDestroyView() super.onDestroyView()
countDownTimer?.cancel()
// Ensure timer is stopped
stopTimer()
_binding = null _binding = null
} }
}
override fun onDetach() {
super.onDetach()
Log.d(TAG, "onDetach - final cleanup")
stopTimer()
}
}
// override fun onDestroyView() {
// super.onDestroyView()
// countDownTimer?.cancel()
// _binding = 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()
// }

View File

@ -104,20 +104,11 @@ class RegisterStep3Fragment : Fragment() {
// Set up button listeners // Set up button listeners
binding.btnPrevious.setOnClickListener { binding.btnPrevious.setOnClickListener {
// Go back to the previous step // Go back to the previous step
// parentFragmentManager.popBackStack() parentFragmentManager.popBackStack()
// (activity as? RegisterActivity)?.navigateToStep(2, null)
// (activity as? RegisterActivity)?.goBackToPreviousStep()
// Option 2: Direct navigation to step 1
val step2Fragment = RegisterStep2Fragment()
parentFragmentManager.beginTransaction()
.replace(R.id.fragment_container, step2Fragment)
.commit()
} }
binding.btnRegister.setOnClickListener { binding.btnRegister.setOnClickListener {
submitAddress() submitAddress()
sessionManager.clearAll()
} }
// If user skips address entry // If user skips address entry
@ -512,7 +503,7 @@ 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(), "Berhasil mendaftarkan akun", Toast.LENGTH_LONG).show() Toast.makeText(requireContext(), "Registration completed successfully!", Toast.LENGTH_LONG).show()
// Navigate to login screen // Navigate to login screen
startActivity(Intent(requireContext(), LoginActivity::class.java)) startActivity(Intent(requireContext(), LoginActivity::class.java))
@ -530,5 +521,4 @@ class RegisterStep3Fragment : Fragment() {
ViewCompat.setWindowInsetsAnimationCallback(binding.root, null) ViewCompat.setWindowInsetsAnimationCallback(binding.root, null)
_binding = null _binding = null
} }
} }

View File

@ -41,16 +41,11 @@ 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)
if (!sessionManager.isLoggedIn()){ sessionManager = SessionManager(this)
binding.emptyCart.text = "Silahkan masuk terlebih dahulu" apiService = ApiConfig.getApiService(sessionManager)
}
WindowCompat.setDecorFitsSystemWindows(window, false) WindowCompat.setDecorFitsSystemWindows(window, false)
@ -123,7 +118,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, "Pilih produk yang sama dengan toko", Toast.LENGTH_SHORT).show() Toast.makeText(this, "Please select items from a single store only", Toast.LENGTH_SHORT).show()
} }
} }
} else { } else {

View File

@ -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, "Silahkan masuk terlebih dahulu", Toast.LENGTH_SHORT).show() Toast.makeText(this, "Please login first", 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, "Gagal memuat produk", Toast.LENGTH_SHORT).show() Toast.makeText(this, "Cannot open product details", 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, "Gambar terlalu besar. Maksimal 1MB", Toast.LENGTH_SHORT).show() Toast.makeText(this, "Image too large (max 5MB)", Toast.LENGTH_SHORT).show()
return return
} }

View File

@ -5,6 +5,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.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
@ -79,10 +80,8 @@ class ChatListFragment : Fragment() {
} }
} }
is Result.Error -> { is Result.Error -> {
// binding.tvEmptyChat.visibility = View.VISIBLE binding.tvEmptyChat.visibility = View.VISIBLE
binding.progressBarChat.visibility = View.VISIBLE Toast.makeText(requireContext(), "Failed to load chats", Toast.LENGTH_SHORT).show()
// 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

View File

@ -6,7 +6,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.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
@ -33,7 +32,6 @@ 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
@ -142,13 +140,12 @@ class HomeFragment : Fragment() {
viewModel.uiState.collect { state -> viewModel.uiState.collect { state ->
when (state) { when (state) {
is HomeUiState.Loading -> { is HomeUiState.Loading -> {
binding.loadingAll.root.visibility = View.VISIBLE binding.loading.root.isVisible = true
binding.error.root.isVisible = false binding.error.root.isVisible = false
binding.home.isVisible = false binding.home.isVisible = false
delay(5000)
} }
is HomeUiState.Success -> { is HomeUiState.Success -> {
binding.loadingAll.root.visibility = View.GONE binding.loading.root.isVisible = false
binding.error.root.isVisible = false binding.error.root.isVisible = false
binding.home.isVisible = true binding.home.isVisible = true
val products = state.products val products = state.products
@ -157,12 +154,10 @@ class HomeFragment : Fragment() {
productAdapter?.updateLimitedProducts(products) productAdapter?.updateLimitedProducts(products)
} }
is HomeUiState.Error -> { is HomeUiState.Error -> {
binding.loadingAll.root.visibility = View.GONE binding.loading.root.isVisible = false
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()
} }

View File

@ -7,15 +7,12 @@ 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
@ -109,11 +106,6 @@ 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 {

View File

@ -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())
} }
} }

View File

@ -154,18 +154,8 @@ class CheckoutActivity : AppCompatActivity() {
// Observe address details // Observe address details
viewModel.addressDetails.observe(this) { address -> viewModel.addressDetails.observe(this) { address ->
if (address != null) { binding.tvPlacesAddress.text = address?.recipient
// Show selected address binding.tvAddress.text = "${address?.street}, ${address?.subdistrict}"
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 ->
@ -182,7 +172,9 @@ 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}")
} }
} }
@ -191,20 +183,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, "Terdapat kendala di pemesanan", Toast.LENGTH_SHORT).show() Toast.makeText(this, message, 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, "Berhasil membuat pesanan", Toast.LENGTH_SHORT).show() Toast.makeText(this, "Order successfully created!", Toast.LENGTH_SHORT).show()
setResult(RESULT_OK) setResult(RESULT_OK)
finish() finish()
} }
@ -214,17 +206,10 @@ 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, "Tidak ditemukan metode pembayaran", Toast.LENGTH_SHORT).show() Toast.makeText(this, "No payment methods available", 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")
@ -281,16 +266,6 @@ 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() {
@ -315,8 +290,7 @@ 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()) {
// Hide empty state and show selected shipping // Display shipping name and service in one line
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"
@ -324,8 +298,6 @@ 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
} }
} }
@ -338,10 +310,10 @@ class CheckoutActivity : AppCompatActivity() {
} }
// Shipping method selection // Shipping method selection
binding.tvShippingOption.setOnClickListener { binding.layoutShippingMethod.setOnClickListener {
val addressId = viewModel.addressDetails.value?.id ?: 0 val addressId = viewModel.addressDetails.value?.id ?: 0
if (addressId <= 0) { if (addressId <= 0) {
Toast.makeText(this, "Silahkan pilih alamat dahulu", Toast.LENGTH_SHORT).show() Toast.makeText(this, "Please select delivery address first", Toast.LENGTH_SHORT).show()
return@setOnClickListener return@setOnClickListener
} }
@ -391,7 +363,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, "Berhasil memilih alamat", Toast.LENGTH_SHORT).show() Toast.makeText(this, "Address selected successfully", Toast.LENGTH_SHORT).show()
} }
} }
} }

View File

@ -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, "Gagal memuat pengiriman", Toast.LENGTH_SHORT).show() Toast.makeText(this, "Missing required shipping information", Toast.LENGTH_SHORT).show()
finish() finish()
return return
} }

View File

@ -52,12 +52,12 @@ class AddressActivity : AppCompatActivity() {
windowInsets windowInsets
} }
viewModel.fetchAddresses()
setupToolbar() setupToolbar()
setupRecyclerView() setupRecyclerView()
setupObservers() setupObservers()
viewModel.fetchAddresses()
} }
@ -126,11 +126,6 @@ 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"
} }

View File

@ -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,6 +122,7 @@ 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()
} }
} }
@ -287,7 +288,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, "Terjadi kendala", Toast.LENGTH_SHORT).show() Toast.makeText(this, "Error: ${e.message}", Toast.LENGTH_SHORT).show()
} }
} }
@ -313,10 +314,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()) {
@ -324,10 +325,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()
@ -366,7 +367,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, "Gagal mengunggah foto", Toast.LENGTH_SHORT).show() Toast.makeText(this, "Error preparing upload: ${e.message}", Toast.LENGTH_SHORT).show()
} }
} }
} }

View File

@ -160,8 +160,7 @@ class PaymentActivity : AppCompatActivity() {
viewModel.error.observe(this) { error -> viewModel.error.observe(this) { error ->
if (error.isNotEmpty()) { if (error.isNotEmpty()) {
Toast.makeText(this, "Gagal melakukan pembayaran", Toast.LENGTH_SHORT).show() Toast.makeText(this, error, Toast.LENGTH_SHORT).show()
Log.e(TAG, "Failed payment: $error")
} }
} }
} }

View File

@ -517,14 +517,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, "Terjadi kendala", Toast.LENGTH_SHORT).show() Toast.makeText(context, "Cannot show cancel order dialog", 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, "Terjadi kendala", Toast.LENGTH_SHORT).show() Toast.makeText(context, "Cannot show cancel order dialog", Toast.LENGTH_SHORT).show()
return return
} }
} }
@ -535,7 +535,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, "Pesanan berhasil dibatalkan", Toast.LENGTH_SHORT).show() Toast.makeText(context, "Order cancelled successfully", Toast.LENGTH_SHORT).show()
} }
) )
@ -547,6 +547,15 @@ 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() Toast.makeText(itemView.context, errorMsg, Toast.LENGTH_SHORT).show()

View File

@ -67,7 +67,7 @@ class CancelOrderBottomSheet(
btnConfirm.setOnClickListener { btnConfirm.setOnClickListener {
if (selectedReason == null) { if (selectedReason == null) {
Toast.makeText(context, "Pilih alasan pembatalan", Toast.LENGTH_SHORT).show() Toast.makeText(context, "Please select a reason", Toast.LENGTH_SHORT).show()
return@setOnClickListener return@setOnClickListener
} }

View File

@ -90,7 +90,7 @@ class CreateReviewActivity : AppCompatActivity() {
) )
}) })
} catch (e: Exception) { } catch (e: Exception) {
Toast.makeText(this, "Gagal memuat ulasan", Toast.LENGTH_SHORT).show() Toast.makeText(this, "Error loading review items", Toast.LENGTH_SHORT).show()
finish() finish()
} }
} else { } else {
@ -110,7 +110,7 @@ class CreateReviewActivity : AppCompatActivity() {
) )
) )
} else { } else {
Toast.makeText(this, "Tidak ada produk untuk direview", Toast.LENGTH_SHORT).show() Toast.makeText(this, "No items to review", Toast.LENGTH_SHORT).show()
finish() finish()
} }
} }

View File

@ -112,17 +112,13 @@ 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
binding.progressBarDetailStore.visibility = View.GONE Toast.makeText(this, "Failed to load store: ${result.exception.message}", Toast.LENGTH_SHORT).show()
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
} }
} }
} }
@ -164,10 +160,6 @@ 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
} }
} }
} }
@ -198,14 +190,12 @@ 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.GONE binding.recyclerViewOtherProducts.visibility = View.VISIBLE
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)
@ -326,13 +316,11 @@ 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(
@ -531,11 +519,7 @@ 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 {

View File

@ -64,9 +64,10 @@ class StoreDetailActivity : AppCompatActivity() {
) )
windowInsets windowInsets
} }
loadData()
setupUI() setupUI()
setupObservers() setupObservers()
loadData()
} }
private fun setupUI() { private fun setupUI() {
@ -87,18 +88,15 @@ 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
} }
} }
} }
@ -111,9 +109,6 @@ 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
} }
} }
} }
@ -151,7 +146,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()
if (ratingValue != null && ratingValue > 0f) { if (ratingValue != null && ratingValue > 0f) {
binding.tvStoreRating.text = String.format("%.1f", ratingValue) binding.tvStoreRating.text = String.format("%.1f", ratingValue)
@ -166,12 +161,10 @@ 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)

View File

@ -1,21 +0,0 @@
package com.alya.ecommerce_serang.ui.profile
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.alya.ecommerce_serang.R
class ChangePasswordActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_change_password)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
}
}

View File

@ -90,8 +90,6 @@ 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() {
@ -108,8 +106,7 @@ class DetailProfileActivity : AppCompatActivity() {
} }
editProfileLauncher.launch(intent) editProfileLauncher.launch(intent)
} ?: run { } ?: run {
Toast.makeText(this, "Akun tidak ditemukan", Toast.LENGTH_SHORT).show() Toast.makeText(this, "Profile data is not available", Toast.LENGTH_SHORT).show()
Log.e("DetailProfileActivity", "Profile data is not available")
} }
} }
} }

View File

@ -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,6 +58,7 @@ 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(
@ -71,57 +72,26 @@ 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 cant 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) { storeDataResponse -> myStoreViewModel.myStoreProfile.observe(viewLifecycleOwner) { store ->
storeDataResponse?.let { storeResponse -> store?.let {
val store = storeResponse.store when (store.storeStatus) {
when (store.approvalStatus) {
"process" -> startActivity(Intent(requireContext(), StoreOnReviewActivity::class.java)) "process" -> startActivity(Intent(requireContext(), StoreOnReviewActivity::class.java))
"rejected" -> startActivity( "active" -> startActivity(Intent(requireContext(), MyStoreActivity::class.java))
Intent(requireContext(), RegisterStoreActivity::class.java).putExtra("REAPPLY", true)) "inactive" -> startActivity(Intent(requireContext(), MyStoreActivity::class.java))
else -> { "suspended" -> startActivity(Intent(requireContext(), StoreSuspendedActivity::class.java))
when(store.storeStatus){ else -> startActivity(Intent(requireContext(), RegisterStoreActivity::class.java))
"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()
@ -145,11 +115,6 @@ 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()
} }
@ -165,8 +130,7 @@ 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")
} }
} }
@ -222,8 +186,6 @@ 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(),
@ -234,11 +196,4 @@ class ProfileFragment : Fragment() {
} }
} }
override fun onResume() {
super.onResume()
viewModel.loadUserProfile()
viewModel.checkStoreUser()
}
} }

View File

@ -2,7 +2,6 @@ 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
@ -25,6 +24,7 @@ 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,6 +33,7 @@ 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
@ -40,9 +41,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 {
@ -53,7 +54,7 @@ class EditProfileCustActivity : AppCompatActivity() {
} }
private val getContent = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> private val getContent = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) { if (result.resultCode == Activity.RESULT_OK) {
val data: Intent? = result.data val data: Intent? = result.data
data?.data?.let { data?.data?.let {
selectedImageUri = it selectedImageUri = it
@ -104,8 +105,8 @@ class EditProfileCustActivity : AppCompatActivity() {
} }
userProfile?.let { userProfile?.let {
currentUser = it
populateFields(it) populateFields(it)
setupClickListeners() setupClickListeners()
observeViewModel() observeViewModel()
} }
@ -117,7 +118,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))
} }
@ -155,7 +156,7 @@ class EditProfileCustActivity : AppCompatActivity() {
} }
binding.btnSave.setOnClickListener { binding.btnSave.setOnClickListener {
if (hasChanged()) confirmUpdate() else finish() saveProfile()
} }
} }
@ -212,38 +213,25 @@ 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
@ -254,10 +242,82 @@ class EditProfileCustActivity : AppCompatActivity() {
phone = phone, phone = phone,
birthDate = serverBirthDate, birthDate = serverBirthDate,
email = email, email = email,
imageUri = imgProfile imageUri = selectedImageUri
) )
} }
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"

View File

@ -59,13 +59,12 @@ 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()
@ -73,6 +72,7 @@ class MyStoreActivity : AppCompatActivity() {
setUpClickListeners() setUpClickListeners()
getCountOrder() getCountOrder()
observeViewModel() observeViewModel()
viewModel.fetchBalance()
fetchBalance() fetchBalance()
} }
@ -206,13 +206,6 @@ class MyStoreActivity : AppCompatActivity() {
} }
} }
override fun onResume() {
super.onResume()
viewModel.loadMyStore()
viewModel.loadMyStoreProducts()
viewModel.fetchBalance()
}
companion object { companion object {
private const val PROFILE_REQUEST_CODE = 100 private const val PROFILE_REQUEST_CODE = 100
} }

View File

@ -27,7 +27,6 @@ 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
@ -36,17 +35,8 @@ 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() {
@ -63,7 +53,6 @@ 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
@ -75,15 +64,6 @@ 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)
@ -109,8 +89,6 @@ 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)
@ -151,140 +129,19 @@ class RegisterStoreActivity : AppCompatActivity() {
viewModel.cityId.observe(this) { validateRequiredFields() } viewModel.cityId.observe(this) { validateRequiredFields() }
viewModel.storeTypeId.observe(this) { validateRequiredFields() } viewModel.storeTypeId.observe(this) { validateRequiredFields() }
if (isReapply) { // Setup register button
binding.btnRegister.text = "Ajukan Kembali" binding.btnRegister.setOnClickListener {
binding.layoutRejected.visibility = View.VISIBLE Log.d(TAG, "Register button clicked")
if (viewModel.validateForm()) {
myStoreViewModel.loadMyStore() Log.d(TAG, "Form validation successful, proceeding with registration")
viewModel.registerStore(this)
myStoreViewModel.myStoreProfile.observe(this) { storeDataResponse -> } else {
storeDataResponse?.let { storeResponse -> Log.e(TAG, "Form validation failed")
val store = storeResponse.store Toast.makeText(this, "Harap lengkapi semua field yang wajib diisi", Toast.LENGTH_SHORT).show()
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
} }
} }
// When cities arrive, select the city, then load subdistricts Log.d(TAG, "onCreate: RegisterStoreActivity setup completed")
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() {
@ -320,11 +177,10 @@ 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
} }
} }
@ -446,7 +302,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()
} }
} }
@ -992,63 +848,6 @@ 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"
} }

View File

@ -91,10 +91,10 @@ class DetailStoreProfileActivity : AppCompatActivity() {
viewModel.fetchStoreTypes() viewModel.fetchStoreTypes()
viewModel.myStoreProfile.observe(this) { viewModel.myStoreProfile.observe(this) {
currentStore = it?.store currentStore = it
currentStoreLoaded = true currentStoreLoaded = true
if (storeTypesLoaded) setupStoreTypeSpinner(storeTypesList) if (storeTypesLoaded) setupStoreTypeSpinner(storeTypesList)
updateUI(it?.store) updateUI(it)
} }
viewModel.storeTypes.observe(this) { viewModel.storeTypes.observe(this) {

View File

@ -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.toDouble().toInt()) } tvSellsProductPrice.text = product?.price?.let { formatPrice(it.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.toDouble().toInt()) } tvSellsProductPrice.text = product?.price?.let { formatPrice(it.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.toDouble().toInt()) } tvSellsPrice.text = order.totalAmount?.let { formatPrice(it.toInt()) }
} }
"paid" -> { "paid" -> {
layoutOrders.visibility = View.GONE layoutOrders.visibility = View.GONE

View File

@ -83,10 +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() // getAllOrderCountsAndNavigate()
} }
@ -211,12 +211,6 @@ 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

View File

@ -10,8 +10,6 @@ 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
@ -25,18 +23,12 @@ 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<StoreResponse?>() private val _myStoreProfile = MutableLiveData<Store?>()
val myStoreProfile: LiveData<StoreResponse?> = _myStoreProfile val myStoreProfile: LiveData<Store?> = _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
@ -55,12 +47,7 @@ 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 -> { is Result.Success -> _myStoreProfile.postValue(result.data)
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
} }

View File

@ -7,11 +7,9 @@ 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
@ -390,34 +388,6 @@ 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"
} }

View File

@ -1,5 +0,0 @@
<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>

View File

@ -1,5 +0,0 @@
<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>

View File

@ -1,5 +0,0 @@
<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>

View File

@ -1,5 +0,0 @@
<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>

View File

@ -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="24dp" /> <corners android:radius="5dp" />
</shape> </shape>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

View File

@ -113,7 +113,6 @@
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" />
@ -123,7 +122,6 @@
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" />

View File

@ -1,102 +0,0 @@
<?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>

View File

@ -34,50 +34,69 @@
android:orientation="vertical"> android:orientation="vertical">
<!-- Delivery Address Section --> <!-- Delivery Address Section -->
<com.google.android.material.card.MaterialCardView <androidx.cardview.widget.CardView
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="16dp" android:layout_marginHorizontal="0dp"
android:layout_marginTop="16dp" android:layout_marginTop="0dp"
app:cardCornerRadius="12dp" app:cardElevation="0dp">
app:cardElevation="2dp"
app:cardBackgroundColor="@color/white"
app:strokeColor="#E0E0E0"
app:strokeWidth="1dp">
<androidx.constraintlayout.widget.ConstraintLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="20dp"> android:orientation="vertical"
android:padding="16dp"
android:background="@color/white">
<!-- Header Row -->
<LinearLayout <LinearLayout
android:id="@+id/address_header" android:layout_width="match_parent"
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"
app:tint="@color/blue_300" /> android:layout_gravity="center_vertical"
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="Alamat Pengiriman" android:text="-"
android:textSize="16sp" android:textSize="14sp"
android:textColor="@android:color/black" android:layout_marginStart="32dp" />
android:fontFamily="@font/dmsans_medium"
android:layout_marginStart="12dp" />
<TextView <TextView
android:id="@+id/tv_change_address" android:id="@+id/tv_change_address"
@ -85,441 +104,169 @@
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>
<!-- Empty Address State --> </androidx.cardview.widget.CardView>
<LinearLayout
android:id="@+id/container_empty_address" <View
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="8dp"
android:orientation="vertical" android:background="@color/black_50" />
android:layout_marginTop="4dp"
android:gravity="center" <!-- Product Items Section -->
android:padding="8dp" <androidx.cardview.widget.CardView
app:layout_constraintTop_toBottomOf="@id/address_header" android:id="@+id/card_product"
app:layout_constraintStart_toStartOf="parent" android:layout_width="match_parent"
app:layout_constraintEnd_toEndOf="parent"> android:layout_height="wrap_content"
app:cardElevation="0dp">
<TextView
android:layout_width="wrap_content" <LinearLayout
android:layout_height="wrap_content" android:layout_width="match_parent"
android:layout_marginTop="4dp" android:layout_height="wrap_content"
android:text="Belum ada alamat dipilih" android:orientation="vertical"
android:fontFamily="@font/dmsans_medium" android:background="@color/white"
android:textColor="#757575" android:padding="16dp">
android:textSize="14sp"
android:gravity="center" /> <androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_product_items"
<TextView android:layout_width="match_parent"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_height="wrap_content" android:layout_marginTop="8dp"
android:layout_marginTop="4dp" tools:listitem="@layout/item_order_seller" />
android:text="Pilih alamat pengiriman untuk melanjutkan" </LinearLayout>
android:textColor="#BDBDBD" </androidx.cardview.widget.CardView>
android:textSize="12sp"
android:gravity="center" /> <View
</LinearLayout> android:layout_width="match_parent"
android:layout_height="8dp"
<!-- Selected Address Content --> android:background="#F5F5F5" />
<LinearLayout
android:id="@+id/container_address" <View
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="8dp"
android:orientation="vertical" android:background="@color/black_50" />
android:layout_marginTop="4dp"
android:visibility="gone" <!-- Shipping Method Section -->
app:layout_constraintTop_toBottomOf="@id/address_header" <LinearLayout
app:layout_constraintStart_toStartOf="parent" android:id="@+id/layout_shipping_method"
app:layout_constraintEnd_toEndOf="parent"> android:layout_width="match_parent"
android:layout_height="wrap_content"
<!-- Address Label --> android:orientation="vertical"
<TextView android:background="@color/white"
android:id="@+id/tv_places_address" android:padding="16dp">
android:layout_width="wrap_content"
android:layout_height="wrap_content" <LinearLayout
android:text="Rumah" android:layout_width="match_parent"
android:textColor="#3D84FF" android:layout_height="wrap_content"
android:textSize="12sp" android:orientation="horizontal"
android:fontFamily="@font/dmsans_medium" android:gravity="center_vertical">
android:background="@drawable/bg_edit_text_background"
android:paddingHorizontal="8dp" <TextView
android:paddingVertical="4dp" android:layout_width="0dp"
tools:text="Rumah" /> android:layout_height="wrap_content"
android:layout_weight="1"
<!-- Full Address --> android:text="Metode Pengiriman"
<TextView android:textSize="14sp" />
android:id="@+id/tv_address"
android:layout_width="match_parent" <TextView
android:layout_height="wrap_content" android:id="@+id/tv_shipping_option"
android:layout_marginTop="8dp" android:layout_width="wrap_content"
android:text="Jl. Raya Serang No. 123, Kecamatan Serang, Kabupaten Serang, Banten 42111" android:layout_height="wrap_content"
android:textSize="14sp" android:text="Opsi Pengiriman"
android:textColor="@android:color/black" android:textColor="#3D84FF"
android:lineSpacingExtra="2dp" android:textSize="14sp" />
tools:text="Jl. Raya Serang No. 123, Kecamatan Serang, Kabupaten Serang, Banten 42111" /> </LinearLayout>
</LinearLayout>
<androidx.cardview.widget.CardView
</androidx.constraintlayout.widget.ConstraintLayout> android:id="@+id/card_shipment"
</com.google.android.material.card.MaterialCardView> android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
<!-- Product Items Section --> android:visibility="gone"
<com.google.android.material.card.MaterialCardView app:cardCornerRadius="8dp"
android:id="@+id/card_product" app:cardElevation="0dp"
android:layout_width="match_parent" app:cardBackgroundColor="#F5F5F5">
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp" <LinearLayout
android:layout_marginTop="8dp" android:layout_width="match_parent"
app:cardBackgroundColor="@color/white" android:layout_height="wrap_content"
app:strokeColor="#E0E0E0" android:orientation="horizontal"
app:strokeWidth="1dp"> android:padding="12dp">
<androidx.constraintlayout.widget.ConstraintLayout <RadioButton
android:layout_width="match_parent" android:id="@+id/rb_jne"
android:layout_height="wrap_content" android:layout_width="wrap_content"
android:padding="20dp"> android:layout_height="wrap_content"
android:checked="true" />
<!-- Header Row -->
<LinearLayout <LinearLayout
android:id="@+id/product_header" android:layout_width="0dp"
android:layout_width="0dp" android:layout_height="wrap_content"
android:layout_height="wrap_content" android:layout_weight="1"
android:orientation="horizontal" android:orientation="vertical"
android:gravity="center_vertical" android:layout_marginStart="8dp">
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" <TextView
app:layout_constraintEnd_toEndOf="parent"> android:id="@+id/tv_courier_name"
android:layout_width="wrap_content"
<ImageView android:layout_height="wrap_content"
android:layout_width="24dp" android:text="JNE"
android:layout_height="24dp" android:textSize="16sp"
android:src="@drawable/baseline_local_grocery_store_24" android:fontFamily="@font/dmsans_medium" />
app:tint="@color/blue_300" />
<TextView
<TextView android:id="@+id/tv_delivery_estimate"
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:text="3 - 4 hari kerja"
android:text="Produk Pesanan" android:textSize="14sp"
android:textSize="16sp" android:textColor="#757575" />
android:textColor="@android:color/black" </LinearLayout>
android:fontFamily="@font/dmsans_medium"
android:layout_marginStart="12dp" /> <TextView
</LinearLayout> android:id="@+id/tv_shipping_price"
android:layout_width="wrap_content"
<!-- Empty Product State --> android:layout_height="wrap_content"
<LinearLayout android:text="Rp15.000"
android:id="@+id/container_empty_products" android:textSize="16sp"
android:layout_width="0dp" android:fontFamily="@font/dmsans_medium"
android:layout_height="wrap_content" android:layout_gravity="center_vertical" />
android:orientation="vertical" </LinearLayout>
android:layout_marginTop="4dp" </androidx.cardview.widget.CardView>
android:gravity="center" </LinearLayout>
android:padding="8dp"
android:visibility="visible" <View
app:layout_constraintTop_toBottomOf="@id/product_header" android:layout_width="match_parent"
app:layout_constraintStart_toStartOf="parent" android:layout_height="8dp"
app:layout_constraintEnd_toEndOf="parent"> android:background="@color/black_50" />
<!-- Payment Method Section -->
<TextView <LinearLayout
android:layout_width="wrap_content" android:id="@+id/layout_payment_method"
android:layout_height="wrap_content" android:layout_width="match_parent"
android:layout_marginTop="4dp" android:layout_height="wrap_content"
android:text="Tidak ada produk" android:orientation="vertical"
android:fontFamily="@font/dmsans_medium" android:background="@color/white"
android:textColor="#757575" android:padding="16dp">
android:textSize="14sp"
android:gravity="center" /> <TextView
android:layout_width="match_parent"
<TextView android:layout_height="wrap_content"
android:layout_width="wrap_content" android:text="Metode Pembayaran"
android:layout_height="wrap_content" android:textSize="14sp"
android:layout_marginTop="2dp" android:layout_marginBottom="8dp" />
android:text="Keranjang belanja kosong"
android:textColor="#BDBDBD" <androidx.recyclerview.widget.RecyclerView
android:textSize="12sp" android:id="@+id/rv_payment_info"
android:gravity="center" /> android:layout_width="match_parent"
</LinearLayout> android:layout_height="wrap_content"
tools:listitem="@layout/item_payment_method" />
<!-- Products RecyclerView --> </LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_product_items"
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" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:id="@+id/card_shipping_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_height="wrap_content"
android:padding="20dp">
<!-- Header Row -->
<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_height="wrap_content"
android:padding="20dp">
<!-- 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 -->
@ -547,7 +294,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="Rp0" android:text="Rp65.000"
android:textSize="14sp" /> android:textSize="14sp" />
</LinearLayout> </LinearLayout>
@ -568,7 +315,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="Rp0" android:text="Rp15.000"
android:textSize="14sp" /> android:textSize="14sp" />
</LinearLayout> </LinearLayout>
@ -595,8 +342,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="Rp0" android:text="Rp75.000"
android:textColor="@color/blue_400" android:textColor="#3D84FF"
android:textSize="16sp" android:textSize="16sp"
android:fontFamily="@font/dmsans_bold" /> android:fontFamily="@font/dmsans_bold" />
</LinearLayout> </LinearLayout>
@ -632,7 +379,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="@color/blue_400" android:textColor="#3D84FF"
android:textSize="18sp" android:textSize="18sp"
android:fontFamily="@font/dmsans_bold" /> android:fontFamily="@font/dmsans_bold" />
</LinearLayout> </LinearLayout>
@ -645,7 +392,7 @@
android:textAllCaps="false" android:textAllCaps="false"
android:paddingHorizontal="32dp" android:paddingHorizontal="32dp"
app:cornerRadius="8dp" app:cornerRadius="8dp"
android:backgroundTint="@color/blue_500" /> android:backgroundTint="#3D84FF" />
</LinearLayout> </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -231,18 +231,6 @@
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="16sp"
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"
@ -397,13 +385,6 @@
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"
@ -496,18 +477,6 @@
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="16sp"
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"

View File

@ -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_100" android:src="@drawable/baseline_account_circle_24"
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,7 +87,6 @@
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"

View File

@ -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_100" android:src="@drawable/baseline_account_circle_24"
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,7 +188,6 @@
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"

View File

@ -136,7 +136,6 @@
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" />

View File

@ -69,35 +69,6 @@
</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"

View File

@ -15,207 +15,156 @@
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<!-- Store Information Card --> <!-- Store Information -->
<com.google.android.material.card.MaterialCardView <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/storeInfoCard" android:id="@+id/storeInfoContainer"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:background="@color/blue_50"
android:layout_marginTop="20dp" android:layout_marginTop="16dp"
android:layout_marginEnd="16dp" android:padding="24dp"
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">
<androidx.constraintlayout.widget.ConstraintLayout <ImageView
android:id="@+id/storeInfoContainer" android:id="@+id/ivStoreImage"
android:layout_width="match_parent" android:layout_width="64dp"
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:padding="24dp"> android:layout_marginStart="16dp"
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" />
<!-- Store Image with Material Card wrapper --> <TextView
<com.google.android.material.card.MaterialCardView android:id="@+id/tvStoreType"
android:id="@+id/storeImageCard" android:layout_width="0dp"
android:layout_width="80dp" android:layout_height="wrap_content"
android:layout_height="80dp" android:layout_marginStart="16dp"
app:cardCornerRadius="16dp" android:layout_marginEnd="16dp"
app:cardElevation="2dp" android:textSize="14sp"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintStart_toEndOf="@id/ivStoreImage"
app:layout_constraintTop_toBottomOf="@id/tvStoreName"
tools:text="Makanan Ringan" />
<ImageView <LinearLayout
android:id="@+id/ivStoreImage" android:id="@+id/storeRatingContainer"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:scaleType="centerCrop" android:layout_marginStart="16dp"
tools:src="@drawable/placeholder_image" /> android:layout_marginTop="4dp"
</com.google.android.material.card.MaterialCardView> android:gravity="center_vertical"
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/tvStoreName" android:id="@+id/tvStoreRating"
android:layout_width="0dp"
android:layout_height="wrap_content"
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:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/storeImageCard"
app:layout_constraintTop_toBottomOf="@id/tvStoreName"
tools:text="Makanan Ringan" />
<!-- Rating Container -->
<LinearLayout
android:id="@+id/storeRatingContainer"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="20dp" android:layout_marginStart="4dp"
android:layout_marginTop="8dp" android:textSize="12sp"
android:gravity="center_vertical" android:textStyle="bold"
android:orientation="horizontal" tools:text="5.0" />
app:layout_constraintStart_toEndOf="@id/storeImageCard" </LinearLayout>
app:layout_constraintTop_toBottomOf="@id/tvStoreType">
<ImageView <TextView
android:id="@+id/ivStoreRatingStar" android:id="@+id/tvStoreLocation"
android:layout_width="16dp" android:layout_width="0dp"
android:layout_height="16dp" android:layout_height="wrap_content"
android:src="@drawable/ic_star" android:layout_marginStart="16dp"
app:tint="@color/yellow" /> android:layout_marginTop="4dp"
android:layout_marginEnd="16dp"
android:textSize="12sp"
app:layout_constraintEnd_toStartOf="@id/tvActiveStatus"
app:layout_constraintStart_toEndOf="@id/ivStoreImage"
app:layout_constraintTop_toBottomOf="@id/storeRatingContainer"
tools:text="Kabupaten Serang" />
<TextView <TextView
android:id="@+id/tvStoreRating" android:id="@+id/tvActiveStatus"
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:paddingStart="4dp"
android:textColor="@android:color/black" android:paddingEnd="4dp"
android:textSize="14sp" android:text="Aktif"
android:textStyle="bold" android:textColor="@android:color/black"
tools:text="4.8" /> android:textSize="12sp"
</LinearLayout> app:layout_constraintBottom_toBottomOf="@id/tvStoreLocation"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/tvStoreLocation" />
<!-- Location and Status Row --> <!-- <ImageButton-->
<LinearLayout <!-- android:id="@+id/btnChevron"-->
android:id="@+id/locationStatusContainer" <!-- android:layout_width="wrap_content"-->
android:layout_width="0dp" <!-- android:layout_height="wrap_content"-->
android:layout_height="wrap_content" <!-- android:background="?attr/selectableItemBackgroundBorderless"-->
android:layout_marginStart="20dp" <!-- android:contentDescription="More"-->
android:layout_marginTop="6dp" <!-- android:src="@drawable/ic_chevron_right"-->
android:layout_marginEnd="16dp" <!-- app:layout_constraintBottom_toBottomOf="parent"-->
android:gravity="center_vertical" <!-- app:layout_constraintEnd_toEndOf="parent"-->
android:orientation="horizontal" <!-- app:layout_constraintTop_toTopOf="parent" />-->
app:layout_constraintEnd_toEndOf="parent" </androidx.constraintlayout.widget.ConstraintLayout>
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="0dp" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="24dp" app:layout_constraintTop_toBottomOf="@id/storeInfoContainer"/>
app:dividerColor="@color/black_200"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/storeInfoCard" />
<ProgressBar
android:id="@+id/progress_bar_detail_prod_item" <!-- Tab Layout: TO DO implement after review -->
android:layout_width="wrap_content" <!-- <com.google.android.material.tabs.TabLayout-->
android:layout_height="wrap_content" <!-- android:id="@+id/tabLayout"-->
android:visibility="gone" <!-- android:layout_width="match_parent"-->
android:layout_gravity="center" <!-- android:layout_height="wrap_content"-->
app:layout_constraintBottom_toBottomOf="parent" <!-- app:layout_constraintTop_toBottomOf="@id/storeInfoContainer"-->
app:layout_constraintEnd_toEndOf="parent" <!-- app:tabIndicatorColor="@color/colorPrimary"-->
app:layout_constraintStart_toStartOf="parent" <!-- app:tabSelectedTextColor="@color/colorPrimary"-->
app:layout_constraintTop_toBottomOf="@id/divider_product"/> <!-- app:tabTextColor="@android:color/darker_gray">-->
<!-- <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="0dp" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginStart="16dp" android:layout_marginTop="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>

View File

@ -168,13 +168,8 @@
app:layout_constraintTop_toBottomOf="@id/searchContainer" /> app:layout_constraintTop_toBottomOf="@id/searchContainer" />
<include <include
android:id="@+id/loadingAll" android:id="@+id/loading"
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"

View File

@ -304,55 +304,6 @@
</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"

View File

@ -50,9 +50,8 @@
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="Masukkan OTP" android:hint="Enter 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>
@ -62,19 +61,9 @@
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="Kirim kode OTP" android:text="Verify"
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"
@ -92,7 +81,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="Kirim Ulang" android:text="Resend"
android:textColor="@color/blue1" android:textColor="@color/blue1"
android:textStyle="bold" /> android:textStyle="bold" />
</LinearLayout> </LinearLayout>

View File

@ -233,12 +233,6 @@
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"
@ -246,14 +240,24 @@
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.Material3.Button.OutlinedButton.Icon"/> style="@style/Widget.MaterialComponents.Button.OutlinedButton"/>
</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 -->

View File

@ -9,9 +9,10 @@
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="center" android:gravity="start"
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
@ -23,54 +24,39 @@
android:layout_height="wrap_content"/> android:layout_height="wrap_content"/>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="vertical">
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 <TextView
android:id="@+id/cost_price" android:id="@+id/courier_name_cost"
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:fontFamily="@font/dmsans_semibold"
android:text="Rp15.000"/> 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> </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_height="match_parent"
android:text="Rp15.0000"/>
</LinearLayout> </LinearLayout>
<View <View

View File

@ -22,8 +22,7 @@
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="Muat Ulang" android:text="Retry"
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"

View File

@ -6,13 +6,12 @@
<ProgressBar <ProgressBar
android:id="@+id/progressBar" android:id="@+id/progressBar"
android:layout_width="64dp" android:layout_width="24dp"
android:layout_height="64dp" android:layout_height="24dp"
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>

View File

@ -269,7 +269,7 @@
</style> </style>
<style name="button.small.active.short"> <style name="button.small.active.short">
<item name="android:layout_width">100dp</item> <item name="android:layout_width">144dp</item>
</style> </style>
<style name="button.small.active.short.only_icon"> <style name="button.small.active.short.only_icon">

BIN
unduh/PasarKlik_v2.apk Normal file

Binary file not shown.