mirror of
https://github.com/shaulascr/ecommerce_serang.git
synced 2025-12-16 07:51:02 +00:00
Compare commits
3 Commits
b6b701fa3b
...
f88a5a46ad
| Author | SHA1 | Date | |
|---|---|---|---|
| f88a5a46ad | |||
| 421c20cc4b | |||
| 792e247eaa |
4
.idea/deploymentTargetSelector.xml
generated
4
.idea/deploymentTargetSelector.xml
generated
@ -4,10 +4,10 @@
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DropdownSelection timestamp="2025-05-08T14:50:55.425322500Z">
|
||||
<DropdownSelection timestamp="2025-08-17T17:32:55.497700100Z">
|
||||
<Target type="DEFAULT_BOOT">
|
||||
<handle>
|
||||
<DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\Gracia Hotmauli\.android\avd\Pixel_8_2_2.avd" />
|
||||
<DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\Gracia Hotmauli\.android\avd\Pixel_9_2.avd" />
|
||||
</handle>
|
||||
</Target>
|
||||
</DropdownSelection>
|
||||
|
||||
@ -124,4 +124,7 @@ dependencies {
|
||||
implementation(platform("com.google.firebase:firebase-bom:33.13.0"))
|
||||
implementation("com.google.firebase:firebase-analytics")
|
||||
implementation("com.google.firebase:firebase-messaging-ktx")
|
||||
|
||||
//Splash screen
|
||||
implementation("androidx.core:core-splashscreen:1.0.0")
|
||||
}
|
||||
|
||||
@ -29,6 +29,9 @@
|
||||
android:theme="@style/Theme.Ecommerce_serang"
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".ui.profile.ChangePasswordActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.auth.ResetPassActivity"
|
||||
android:exported="false" />
|
||||
|
||||
@ -10,9 +10,6 @@ data class Store(
|
||||
@field:SerializedName("store_status")
|
||||
val storeStatus: String,
|
||||
|
||||
@field:SerializedName("sppirt")
|
||||
val sppirt: String,
|
||||
|
||||
@field:SerializedName("user_name")
|
||||
val userName: String,
|
||||
|
||||
@ -37,9 +34,6 @@ data class Store(
|
||||
@field:SerializedName("user_phone")
|
||||
val userPhone: String,
|
||||
|
||||
@field:SerializedName("halal")
|
||||
val halal: String,
|
||||
|
||||
@field:SerializedName("id")
|
||||
val id: Int,
|
||||
|
||||
|
||||
@ -1,18 +1,13 @@
|
||||
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
|
||||
|
||||
data class StoreResponse(
|
||||
val message: String,
|
||||
val store: Store
|
||||
)
|
||||
|
||||
data class Store(
|
||||
@SerializedName("store_id") val storeId: Int,
|
||||
@SerializedName("store_status") val storeStatus: String,
|
||||
@SerializedName("store_name") val storeName: String,
|
||||
@SerializedName("user_name") val userName: String,
|
||||
val email: String,
|
||||
@SerializedName("user_phone") val userPhone: String,
|
||||
val balance: String
|
||||
val store: Store,
|
||||
val shipping: List<Shipping> = emptyList(),
|
||||
val payment: List<Payment> = emptyList()
|
||||
)
|
||||
@ -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.Store
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.ListStoreTypeResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.product.StoreResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.store.StoreResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.store.profile.StoreDataResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.store.sells.OrderListResponse
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiService
|
||||
@ -15,13 +15,13 @@ import retrofit2.Response
|
||||
import java.io.IOException
|
||||
|
||||
class MyStoreRepository(private val apiService: ApiService) {
|
||||
suspend fun fetchMyStoreProfile(): Result<Store?> {
|
||||
suspend fun fetchMyStoreProfile(): Result<StoreResponse?> {
|
||||
return try {
|
||||
val response = apiService.getStore()
|
||||
val response = apiService.getMyStoreData()
|
||||
|
||||
if (response.isSuccessful) {
|
||||
val storeResponse: StoreResponse? = response.body()
|
||||
Result.Success(storeResponse?.store)
|
||||
val storeResponse = response.body()
|
||||
Result.Success(storeResponse)
|
||||
} else {
|
||||
val errorMessage = response.errorBody()?.string() ?: "Unknown API error"
|
||||
Log.e("MyStoreRepository", "Error: $errorMessage")
|
||||
|
||||
@ -72,7 +72,7 @@ class LoginActivity : AppCompatActivity() {
|
||||
val password = binding.etLoginPassword.text.toString()
|
||||
|
||||
if (email.isEmpty() || password.isEmpty()) {
|
||||
Toast.makeText(this, "Please fill in all fields", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, "Mohon masukkan email atau password dengan benar", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
loginViewModel.login(email, password)
|
||||
}
|
||||
@ -100,14 +100,14 @@ class LoginActivity : AppCompatActivity() {
|
||||
retrieveFCMToken()
|
||||
// sessionManager.saveUserId(response.userId)
|
||||
|
||||
Toast.makeText(this, "Login Successful", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, "Berhasil masuk", Toast.LENGTH_SHORT).show()
|
||||
|
||||
startActivity(Intent(this, MainActivity::class.java))
|
||||
finish()
|
||||
}
|
||||
is com.alya.ecommerce_serang.data.repository.Result.Error -> {
|
||||
Log.e("LoginActivity", "Login Failed: ${result.exception.message}")
|
||||
Toast.makeText(this, "Login Failed: ${result.exception.message}", Toast.LENGTH_LONG).show()
|
||||
Toast.makeText(this, "Gagal masuk", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
is Result.Loading -> {
|
||||
// Show loading state
|
||||
|
||||
@ -29,7 +29,7 @@ class OtpBottomSheetDialog(
|
||||
onRegister(updatedUserData) // Send full data to ViewModel
|
||||
dismiss() // Close dialog
|
||||
} else {
|
||||
Toast.makeText(requireContext(), "Please enter OTP", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(requireContext(), "Silahkan masukkan kode OTP", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
return view
|
||||
|
||||
@ -86,7 +86,6 @@ class RegisterActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
// navigate step register in fragment
|
||||
fun navigateToStep(step: Int, userData: RegisterRequest?) {
|
||||
val fragment = when (step) {
|
||||
1 -> RegisterStep1Fragment.newInstance()
|
||||
|
||||
@ -111,7 +111,7 @@ class ResetPassActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun handleError(errorMessage: String) {
|
||||
Toast.makeText(this, "Error: $errorMessage", Toast.LENGTH_LONG).show()
|
||||
Log.e(TAG, "Error: $errorMessage")
|
||||
|
||||
// Optionally show error dialog
|
||||
AlertDialog.Builder(this)
|
||||
|
||||
@ -155,19 +155,20 @@ class RegisterStep1Fragment : Fragment() {
|
||||
"email" -> {
|
||||
isEmailValid = isValid
|
||||
if (!isValid) {
|
||||
Toast.makeText(requireContext(), "Email is already registered", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(requireContext(), "Email sudah digunakan. Gunakan email lainnya.", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
"phone" -> {
|
||||
isPhoneValid = isValid
|
||||
if (!isValid) {
|
||||
Toast.makeText(requireContext(), "Phone number is already registered", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(requireContext(), "Nomor handphone sudah digunakan. Gunakan nomor lainnya. ", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is com.alya.ecommerce_serang.data.repository.Result.Error -> {
|
||||
Toast.makeText(requireContext(), "Validation failed: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(requireContext(), "Gagal melakukan validasi", Toast.LENGTH_SHORT).show()
|
||||
Log.e(TAG, "Validation failed: ${result.exception.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -200,7 +201,8 @@ class RegisterStep1Fragment : Fragment() {
|
||||
is Result.Error -> {
|
||||
binding.progressBar.visibility = View.GONE
|
||||
binding.btnNext.isEnabled = true
|
||||
Toast.makeText(requireContext(), "OTP Request Failed: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
||||
Log.e(TAG, "OTP Request Failed: ${result.exception.message}")
|
||||
Toast.makeText(requireContext(), "Gagal mendapatkan OTP. Kirim ulang OTP", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -229,13 +231,13 @@ class RegisterStep1Fragment : Fragment() {
|
||||
// Check if all fields are filled
|
||||
if (email.isEmpty() || password.isEmpty() || confirmPassword.isEmpty() || phone.isEmpty() ||
|
||||
username.isEmpty() || fullName.isEmpty() || birthDate.isEmpty()) {
|
||||
Toast.makeText(requireContext(), "Please fill all required fields", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(requireContext(), "Silahkan lengkapi seluruh isian", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
// Check if passwords match
|
||||
if (password != confirmPassword) {
|
||||
Toast.makeText(requireContext(), "Passwords do not match", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(requireContext(), "Konfirmasi kata sandi tidak sesua. Periksa kembali", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
@ -253,7 +255,7 @@ class RegisterStep1Fragment : Fragment() {
|
||||
if (isEmailValid && isPhoneValid) {
|
||||
requestOtp(email)
|
||||
} else {
|
||||
Toast.makeText(requireContext(), "Please fix validation errors before proceeding", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(requireContext(), "Silahkan perbaiki data yang dimasukkan", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.alya.ecommerce_serang.ui.auth.fragments
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.CountDownTimer
|
||||
@ -13,6 +14,7 @@ import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
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.retrofit.ApiConfig
|
||||
import com.alya.ecommerce_serang.data.repository.OrderRepository
|
||||
@ -24,6 +26,7 @@ import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||
import com.alya.ecommerce_serang.utils.SessionManager
|
||||
import com.alya.ecommerce_serang.utils.viewmodel.RegisterViewModel
|
||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
import com.google.firebase.messaging.FirebaseMessaging
|
||||
|
||||
class RegisterStep2Fragment : Fragment() {
|
||||
private var _binding: FragmentRegisterStep2Binding? = null
|
||||
@ -109,6 +112,10 @@ class RegisterStep2Fragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
binding.btnBack.setOnClickListener {
|
||||
parentFragmentManager.popBackStack()
|
||||
}
|
||||
|
||||
observeRegistrationState()
|
||||
observeLoginState()
|
||||
Log.d(TAG, "Registration and login state observers set up")
|
||||
@ -129,11 +136,6 @@ class RegisterStep2Fragment : Fragment() {
|
||||
Log.d(TAG, "Updating user data with OTP: $otp")
|
||||
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)
|
||||
} ?: Log.e(TAG, "userData is null, cannot proceed with verification")
|
||||
}
|
||||
@ -250,6 +252,8 @@ class RegisterStep2Fragment : Fragment() {
|
||||
// Save the token in fragment
|
||||
val accessToken = result.data.accessToken
|
||||
sessionManager.saveToken(accessToken)
|
||||
retrieveFCMToken()
|
||||
|
||||
Log.d(TAG, "Token saved to SessionManager: $accessToken")
|
||||
|
||||
// Proceed to Step 3
|
||||
@ -279,6 +283,37 @@ 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")
|
||||
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
countDownTimer?.cancel()
|
||||
|
||||
@ -104,11 +104,20 @@ class RegisterStep3Fragment : Fragment() {
|
||||
// Set up button listeners
|
||||
binding.btnPrevious.setOnClickListener {
|
||||
// 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 {
|
||||
submitAddress()
|
||||
sessionManager.clearAll()
|
||||
}
|
||||
|
||||
// If user skips address entry
|
||||
@ -503,7 +512,7 @@ class RegisterStep3Fragment : Fragment() {
|
||||
|
||||
private fun showRegistrationSuccess() {
|
||||
// Now we can show the success message for the overall registration process
|
||||
Toast.makeText(requireContext(), "Registration completed successfully!", Toast.LENGTH_LONG).show()
|
||||
Toast.makeText(requireContext(), "Berhasil mendaftarkan akun", Toast.LENGTH_LONG).show()
|
||||
|
||||
// Navigate to login screen
|
||||
startActivity(Intent(requireContext(), LoginActivity::class.java))
|
||||
@ -521,4 +530,5 @@ class RegisterStep3Fragment : Fragment() {
|
||||
ViewCompat.setWindowInsetsAnimationCallback(binding.root, null)
|
||||
_binding = null
|
||||
}
|
||||
|
||||
}
|
||||
@ -41,11 +41,16 @@ class CartActivity : AppCompatActivity() {
|
||||
}
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
sessionManager = SessionManager(this)
|
||||
apiService = ApiConfig.getApiService(sessionManager)
|
||||
|
||||
|
||||
binding = ActivityCartBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
sessionManager = SessionManager(this)
|
||||
apiService = ApiConfig.getApiService(sessionManager)
|
||||
if (!sessionManager.isLoggedIn()){
|
||||
binding.emptyCart.text = "Silahkan masuk terlebih dahulu"
|
||||
}
|
||||
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
@ -118,7 +123,7 @@ class CartActivity : AppCompatActivity() {
|
||||
// Start checkout with the prepared items
|
||||
startCheckoutWithWholesaleInfo(selectedItems)
|
||||
} else {
|
||||
Toast.makeText(this, "Please select items from a single store only", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, "Pilih produk yang sama dengan toko", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -124,7 +124,7 @@ class ChatActivity : AppCompatActivity() {
|
||||
|
||||
if (token.isEmpty()) {
|
||||
// User not logged in, redirect to login
|
||||
Toast.makeText(this, "Please login first", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, "Silahkan masuk terlebih dahulu", Toast.LENGTH_SHORT).show()
|
||||
startActivity(Intent(this, LoginActivity::class.java))
|
||||
finish()
|
||||
return
|
||||
@ -506,7 +506,7 @@ class ChatActivity : AppCompatActivity() {
|
||||
}
|
||||
startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(this, "Cannot open product details", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, "Gagal memuat produk", Toast.LENGTH_SHORT).show()
|
||||
Log.e(TAG, "Error navigating to product detail", e)
|
||||
}
|
||||
}
|
||||
@ -622,7 +622,7 @@ class ChatActivity : AppCompatActivity() {
|
||||
if (outputFile.exists() && outputFile.length() > 0) {
|
||||
if (outputFile.length() > 5 * 1024 * 1024) {
|
||||
Log.e(TAG, "File too large: ${outputFile.length()} bytes")
|
||||
Toast.makeText(this, "Image too large (max 5MB)", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, "Gambar terlalu besar. Maksimal 1MB", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,6 @@ import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||
@ -80,8 +79,10 @@ class ChatListFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
is Result.Error -> {
|
||||
binding.tvEmptyChat.visibility = View.VISIBLE
|
||||
Toast.makeText(requireContext(), "Failed to load chats", Toast.LENGTH_SHORT).show()
|
||||
// binding.tvEmptyChat.visibility = View.VISIBLE
|
||||
binding.progressBarChat.visibility = View.VISIBLE
|
||||
// Toast.makeText(requireContext(), "Failed to load chats", Toast.LENGTH_SHORT).show()
|
||||
Log.e(TAG, "Failed to load chats")
|
||||
}
|
||||
Result.Loading -> {
|
||||
binding.progressBarChat.visibility = View.VISIBLE
|
||||
|
||||
@ -196,7 +196,7 @@ class CheckoutActivity : AppCompatActivity() {
|
||||
// Observe order creation
|
||||
viewModel.orderCreated.observe(this) { created ->
|
||||
if (created) {
|
||||
Toast.makeText(this, "Order successfully created!", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, "Berhasil membuat pesanan", Toast.LENGTH_SHORT).show()
|
||||
setResult(RESULT_OK)
|
||||
finish()
|
||||
}
|
||||
@ -206,10 +206,12 @@ class CheckoutActivity : AppCompatActivity() {
|
||||
private fun setupPaymentMethodsRecyclerView(paymentMethods: List<DetailPaymentItem>) {
|
||||
if (paymentMethods.isEmpty()) {
|
||||
Log.e("CheckoutActivity", "Payment methods list is empty")
|
||||
Toast.makeText(this, "No payment methods available", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, "Tidak ditemukan metode pembayaran", Toast.LENGTH_SHORT).show()
|
||||
binding.tvEmptyPayment.visibility = View.VISIBLE
|
||||
return
|
||||
}
|
||||
|
||||
binding.tvEmptyPayment.visibility = View.GONE
|
||||
// Debug logging
|
||||
Log.d("CheckoutActivity", "Setting up payment methods: ${paymentMethods.size} methods available")
|
||||
|
||||
@ -313,7 +315,7 @@ class CheckoutActivity : AppCompatActivity() {
|
||||
binding.layoutShippingMethod.setOnClickListener {
|
||||
val addressId = viewModel.addressDetails.value?.id ?: 0
|
||||
if (addressId <= 0) {
|
||||
Toast.makeText(this, "Please select delivery address first", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, "Silahkan pilih metode pengiriman dahulu", Toast.LENGTH_SHORT).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
@ -363,7 +365,7 @@ class CheckoutActivity : AppCompatActivity() {
|
||||
viewModel.setSelectedAddress(addressId)
|
||||
|
||||
// You might want to show a toast or some UI feedback
|
||||
Toast.makeText(this, "Address selected successfully", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, "Berhasil memilih alamat", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,7 +67,7 @@ class ShippingActivity : AppCompatActivity() {
|
||||
// Validate required information
|
||||
if (addressId <= 0 || productId <= 0) {
|
||||
Log.e(TAG, "Missing required shipping information: addressId=$addressId, productId=$productId")
|
||||
Toast.makeText(this, "Missing required shipping information", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, "Gagal memuat pengiriman", Toast.LENGTH_SHORT).show()
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
@ -122,7 +122,6 @@ class AddEvidencePaymentActivity : AppCompatActivity() {
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "ERROR in AddEvidencePaymentActivity onCreate: ${e.message}", e)
|
||||
Toast.makeText(this, "Error: ${e.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
@ -288,7 +287,7 @@ class AddEvidencePaymentActivity : AppCompatActivity() {
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error handling selected image", e)
|
||||
Toast.makeText(this, "Error: ${e.message}", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, "Terjadi kendala", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
@ -367,7 +366,7 @@ class AddEvidencePaymentActivity : AppCompatActivity() {
|
||||
viewModel.uploadPaymentProof(request)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error creating upload request: ${e.message}", e)
|
||||
Toast.makeText(this, "Error preparing upload: ${e.message}", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, "Gagal mengunggah foto", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -160,7 +160,8 @@ class PaymentActivity : AppCompatActivity() {
|
||||
|
||||
viewModel.error.observe(this) { error ->
|
||||
if (error.isNotEmpty()) {
|
||||
Toast.makeText(this, error, Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, "Gagal melakukan pembayaran", Toast.LENGTH_SHORT).show()
|
||||
Log.e(TAG, "Failed payment: $error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -517,14 +517,14 @@ class OrderHistoryAdapter(
|
||||
} else {
|
||||
// Log error and show a Toast instead if we can't get a FragmentManager
|
||||
Log.e("OrderHistoryAdapter", "Cannot show bottom sheet: Context is not a FragmentActivity")
|
||||
Toast.makeText(context, "Cannot show cancel order dialog", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(context, "Terjadi kendala", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// Log error and show a Toast instead if we can't get a FragmentManager
|
||||
Log.e("OrderHistoryAdapter", "Cannot show bottom sheet: Context is not a FragmentActivity")
|
||||
Toast.makeText(context, "Cannot show cancel order dialog", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(context, "Terjadi kendala", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -535,7 +535,7 @@ class OrderHistoryAdapter(
|
||||
onOrderCancelled = {
|
||||
callbacks.onOrderCancelled(orderId.toString(), true, "Order cancelled successfully")
|
||||
// Show a success message
|
||||
Toast.makeText(context, "Order cancelled successfully", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(context, "Pesanan berhasil dibatalkan", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@ -67,7 +67,7 @@ class CancelOrderBottomSheet(
|
||||
|
||||
btnConfirm.setOnClickListener {
|
||||
if (selectedReason == null) {
|
||||
Toast.makeText(context, "Please select a reason", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(context, "Pilih alasan pembatalan", Toast.LENGTH_SHORT).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
|
||||
@ -90,7 +90,7 @@ class CreateReviewActivity : AppCompatActivity() {
|
||||
)
|
||||
})
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(this, "Error loading review items", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, "Gagal memuat ulasan", Toast.LENGTH_SHORT).show()
|
||||
finish()
|
||||
}
|
||||
} else {
|
||||
@ -110,7 +110,7 @@ class CreateReviewActivity : AppCompatActivity() {
|
||||
)
|
||||
)
|
||||
} else {
|
||||
Toast.makeText(this, "No items to review", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, "Tidak ada produk untuk direview", Toast.LENGTH_SHORT).show()
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -106,7 +106,8 @@ class DetailProfileActivity : AppCompatActivity() {
|
||||
}
|
||||
editProfileLauncher.launch(intent)
|
||||
} ?: run {
|
||||
Toast.makeText(this, "Profile data is not available", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, "Akun tidak ditemukan", Toast.LENGTH_SHORT).show()
|
||||
Log.e("DetailProfileActivity", "Profile data is not available")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,10 +20,10 @@ import com.alya.ecommerce_serang.data.repository.MyStoreRepository
|
||||
import com.alya.ecommerce_serang.data.repository.UserRepository
|
||||
import com.alya.ecommerce_serang.databinding.FragmentProfileBinding
|
||||
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.history.HistoryActivity
|
||||
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.StoreSuspendedActivity
|
||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||
@ -58,7 +58,6 @@ class ProfileFragment : Fragment() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
sessionManager = SessionManager(requireContext())
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
@ -72,6 +71,30 @@ class ProfileFragment : Fragment() {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
sessionManager = SessionManager(requireContext())
|
||||
|
||||
if (!sessionManager.isLoggedIn()) {
|
||||
// Redirect to LoginActivity
|
||||
binding.tvName.text = "Selamat Datang"
|
||||
binding.tvUsername.text = "Silahkan masuk"
|
||||
binding.btnDetailProfile.text = "Masuk"
|
||||
binding.btnDetailProfile.setOnClickListener {
|
||||
val intent = Intent(requireContext(), LoginActivity::class.java)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
startActivity(intent)
|
||||
|
||||
// ✅ Finish the host activity so user can’t go back
|
||||
requireActivity().finish()
|
||||
}
|
||||
|
||||
binding.containerBukaToko.visibility = View.GONE
|
||||
binding.cardPesanan.visibility = View.GONE
|
||||
binding.tvPengaturanAkun.visibility = View.GONE
|
||||
binding.containerSettings.visibility = View.GONE
|
||||
binding.cardAbout.visibility = View.GONE
|
||||
binding.cardLogout.visibility = View.GONE
|
||||
}
|
||||
|
||||
observeUserProfile()
|
||||
|
||||
observeStoreStatus()
|
||||
@ -84,14 +107,19 @@ class ProfileFragment : Fragment() {
|
||||
// else startActivity(Intent(requireContext(), RegisterStoreActivity::class.java))
|
||||
if (viewModel.checkStore.value == true) {
|
||||
myStoreViewModel.loadMyStore()
|
||||
myStoreViewModel.myStoreProfile.observe(viewLifecycleOwner) { store ->
|
||||
store?.let {
|
||||
when (store.storeStatus) {
|
||||
myStoreViewModel.myStoreProfile.observe(viewLifecycleOwner) { storeDataResponse ->
|
||||
storeDataResponse?.let { storeResponse ->
|
||||
val store = storeResponse.store
|
||||
when (store.approvalStatus) {
|
||||
"process" -> startActivity(Intent(requireContext(), StoreOnReviewActivity::class.java))
|
||||
"active" -> startActivity(Intent(requireContext(), MyStoreActivity::class.java))
|
||||
"inactive" -> startActivity(Intent(requireContext(), MyStoreActivity::class.java))
|
||||
"rejected" -> startActivity(
|
||||
Intent(requireContext(), RegisterStoreActivity::class.java).putExtra("REAPPLY", true))
|
||||
else -> {
|
||||
when(store.storeStatus){
|
||||
"suspended" -> startActivity(Intent(requireContext(), StoreSuspendedActivity::class.java))
|
||||
else -> startActivity(Intent(requireContext(), RegisterStoreActivity::class.java))
|
||||
else -> startActivity(Intent(requireContext(), MyStoreActivity::class.java))
|
||||
}
|
||||
}
|
||||
}
|
||||
} ?: run {
|
||||
Toast.makeText(requireContext(), "Gagal memuat data toko", Toast.LENGTH_SHORT).show()
|
||||
@ -115,6 +143,11 @@ class ProfileFragment : Fragment() {
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
binding.cardChangePass.setOnClickListener{
|
||||
val intent = Intent(requireContext(), ChangePasswordActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
binding.cardLogout.setOnClickListener{
|
||||
logout()
|
||||
}
|
||||
@ -130,7 +163,8 @@ class ProfileFragment : Fragment() {
|
||||
user?.let { updateUI(it) }
|
||||
}
|
||||
viewModel.errorMessage.observe(viewLifecycleOwner) { errorMessage ->
|
||||
Toast.makeText(requireContext(), errorMessage, Toast.LENGTH_SHORT).show()
|
||||
// Toast.makeText(requireContext(), errorMessage, Toast.LENGTH_SHORT).show()
|
||||
Log.e("Profile Fragment", "Failed to load profile: $errorMessage")
|
||||
}
|
||||
}
|
||||
|
||||
@ -186,6 +220,8 @@ class ProfileFragment : Fragment() {
|
||||
sessionManager.clearAll()
|
||||
val intent = Intent(requireContext(), LoginActivity::class.java)
|
||||
startActivity(intent)
|
||||
requireActivity().finish()
|
||||
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
|
||||
@ -63,7 +63,7 @@ class MyStoreActivity : AppCompatActivity() {
|
||||
viewModel.loadMyStoreProducts()
|
||||
|
||||
viewModel.myStoreProfile.observe(this){ user ->
|
||||
user?.let { myStoreProfileOverview(it) }
|
||||
user?.let { myStoreProfileOverview(it.store) }
|
||||
}
|
||||
|
||||
viewModel.errorMessage.observe(this) { error ->
|
||||
|
||||
@ -27,6 +27,7 @@ import androidx.core.view.WindowInsetsCompat
|
||||
import com.alya.ecommerce_serang.R
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.StoreTypesItem
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||
import com.alya.ecommerce_serang.data.repository.MyStoreRepository
|
||||
import com.alya.ecommerce_serang.data.repository.Result
|
||||
import com.alya.ecommerce_serang.data.repository.UserRepository
|
||||
import com.alya.ecommerce_serang.databinding.ActivityRegisterStoreBinding
|
||||
@ -35,8 +36,17 @@ import com.alya.ecommerce_serang.ui.order.address.CityAdapter
|
||||
import com.alya.ecommerce_serang.ui.order.address.ProvinceAdapter
|
||||
import com.alya.ecommerce_serang.ui.order.address.SubdsitrictAdapter
|
||||
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.viewmodel.MyStoreViewModel
|
||||
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() {
|
||||
|
||||
@ -53,6 +63,7 @@ class RegisterStoreActivity : AppCompatActivity() {
|
||||
private val PICK_KTP_REQUEST = 1002
|
||||
private val PICK_NPWP_REQUEST = 1003
|
||||
private val PICK_NIB_REQUEST = 1004
|
||||
private var isReapply: Boolean = false
|
||||
|
||||
// Location request code
|
||||
private val LOCATION_PERMISSION_REQUEST = 2001
|
||||
@ -64,6 +75,15 @@ class RegisterStoreActivity : AppCompatActivity() {
|
||||
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?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityRegisterStoreBinding.inflate(layoutInflater)
|
||||
@ -89,6 +109,8 @@ class RegisterStoreActivity : AppCompatActivity() {
|
||||
|
||||
setupHeader()
|
||||
|
||||
isReapply = intent.getBooleanExtra("REAPPLY", false)
|
||||
|
||||
provinceAdapter = ProvinceAdapter(this)
|
||||
cityAdapter = CityAdapter(this)
|
||||
subdistrictAdapter = SubdsitrictAdapter(this)
|
||||
@ -129,19 +151,140 @@ class RegisterStoreActivity : AppCompatActivity() {
|
||||
viewModel.cityId.observe(this) { validateRequiredFields() }
|
||||
viewModel.storeTypeId.observe(this) { validateRequiredFields() }
|
||||
|
||||
// Setup register button
|
||||
binding.btnRegister.setOnClickListener {
|
||||
Log.d(TAG, "Register button clicked")
|
||||
if (viewModel.validateForm()) {
|
||||
Log.d(TAG, "Form validation successful, proceeding with registration")
|
||||
viewModel.registerStore(this)
|
||||
} else {
|
||||
Log.e(TAG, "Form validation failed")
|
||||
Toast.makeText(this, "Harap lengkapi semua field yang wajib diisi", Toast.LENGTH_SHORT).show()
|
||||
if (isReapply) {
|
||||
binding.btnRegister.text = "Ajukan Kembali"
|
||||
binding.layoutRejected.visibility = View.VISIBLE
|
||||
|
||||
myStoreViewModel.loadMyStore()
|
||||
|
||||
myStoreViewModel.myStoreProfile.observe(this) { storeDataResponse ->
|
||||
storeDataResponse?.let { storeResponse ->
|
||||
val store = storeResponse.store
|
||||
binding.tvRejectedReason.text = store.approvalReason
|
||||
|
||||
// Prefill basic fields
|
||||
binding.etStoreName.setText(store.storeName)
|
||||
binding.etStoreDescription.setText(store.storeDescription)
|
||||
binding.etStreet.setText(store.street)
|
||||
binding.etPostalCode.setText(store.postalCode)
|
||||
binding.etAddressDetail.setText(store.detail)
|
||||
|
||||
viewModel.storeName.value = store.storeName
|
||||
viewModel.storeDescription.value = store.storeDescription
|
||||
viewModel.street.value = store.street
|
||||
viewModel.postalCode.value = store.postalCode.toIntOrNull() ?: 0
|
||||
viewModel.addressDetail.value = store.detail
|
||||
|
||||
// Prefill bank info
|
||||
storeResponse.payment.firstOrNull()?.let { payment ->
|
||||
viewModel.bankName.value = payment.bankName
|
||||
viewModel.bankNumber.value = payment.bankNum.toIntOrNull() ?: 0
|
||||
val bankPosition = bankAdapter.findPositionByName(payment.bankName)
|
||||
binding.spinnerBankName.setSelection(bankPosition, false)
|
||||
}
|
||||
|
||||
// Prefill couriers
|
||||
storeResponse.shipping.forEach { courier ->
|
||||
when (courier.courier) {
|
||||
"jne" -> binding.checkboxJne.isChecked = true
|
||||
"pos" -> binding.checkboxPos.isChecked = true
|
||||
"tiki" -> binding.checkboxTiki.isChecked = true
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "onCreate: RegisterStoreActivity setup completed")
|
||||
// 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
|
||||
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() {
|
||||
@ -177,10 +320,11 @@ class RegisterStoreActivity : AppCompatActivity() {
|
||||
if (isFormValid) {
|
||||
binding.btnRegister.setBackgroundResource(R.drawable.bg_button_active)
|
||||
binding.btnRegister.setTextColor(ContextCompat.getColor(this, R.color.white))
|
||||
binding.btnRegister.isEnabled = true
|
||||
} else {
|
||||
binding.btnRegister.setBackgroundResource(R.drawable.bg_button_disabled)
|
||||
binding.btnRegister.setTextColor(ContextCompat.getColor(this, R.color.black_300))
|
||||
|
||||
binding.btnRegister.isEnabled = false
|
||||
}
|
||||
}
|
||||
|
||||
@ -302,7 +446,7 @@ class RegisterStoreActivity : AppCompatActivity() {
|
||||
viewModel.errorMessage.observe(this) { errorMsg ->
|
||||
if (errorMsg.isNotEmpty()) {
|
||||
Log.e(TAG, "setupStoreTypesObserver: Error loading store types: $errorMsg")
|
||||
Toast.makeText(this, "Error loading store types: $errorMsg", Toast.LENGTH_SHORT).show()
|
||||
// Toast.makeText(this, "Error loading store types: $errorMsg", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
@ -848,6 +992,63 @@ class RegisterStoreActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun doUpdateStoreProfile() {
|
||||
val nameBody: RequestBody = (viewModel.storeName.value ?: "")
|
||||
.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||
val typeBody: RequestBody = ((viewModel.storeTypeId.value ?: 0).toString())
|
||||
.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||
val descBody: RequestBody = (viewModel.storeDescription.value ?: "")
|
||||
.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||
val onLeaveBody: RequestBody = "false"
|
||||
.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||
|
||||
// --- Build Multipart for store image (optional) ---
|
||||
// Prefer compressing images to keep payload small; fall back to raw copy if needed.
|
||||
val storeImgPart: MultipartBody.Part? = viewModel.storeImageUri?.let { uri ->
|
||||
try {
|
||||
// (A) Optional safety check: only allow jpg/png/webp
|
||||
val allowed = Regex("^(jpg|jpeg|png|webp)$", RegexOption.IGNORE_CASE)
|
||||
if (!ImageUtils.isAllowedFileType(this, uri, allowed)) {
|
||||
Toast.makeText(this, "Format gambar tidak didukung", Toast.LENGTH_SHORT).show()
|
||||
null
|
||||
} else {
|
||||
// (B) Compress for upload (ke cacheDir), then build multipart
|
||||
val compressed: File = ImageUtils.compressImage(
|
||||
context = this,
|
||||
uri = uri,
|
||||
filename = "storeimg", // prefix
|
||||
maxWidth = 1024,
|
||||
maxHeight = 1024,
|
||||
quality = 80
|
||||
)
|
||||
FileUtils.createMultipartFromFile("storeimg", compressed)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// If compression fails, try raw copy as fallback
|
||||
val rawFile = FileUtils.createTempFileFromUri(this, uri)
|
||||
rawFile?.let { FileUtils.createMultipartFromFile("storeimg", it) }
|
||||
}
|
||||
}
|
||||
|
||||
myStoreViewModel.updateStoreProfile(
|
||||
storeName = nameBody,
|
||||
storeType = typeBody,
|
||||
description = descBody,
|
||||
isOnLeave = onLeaveBody,
|
||||
storeImage = storeImgPart
|
||||
)
|
||||
|
||||
myStoreViewModel.updateStoreProfileResult.observe(this) {
|
||||
Toast.makeText(this, "Pengajuan ulang berhasil dikirim", Toast.LENGTH_SHORT).show()
|
||||
finish()
|
||||
}
|
||||
myStoreViewModel.errorMessage.observe(this) {
|
||||
if (!it.isNullOrEmpty()) {
|
||||
Toast.makeText(this, it, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "RegisterStoreActivity"
|
||||
}
|
||||
|
||||
@ -91,10 +91,10 @@ class DetailStoreProfileActivity : AppCompatActivity() {
|
||||
viewModel.fetchStoreTypes()
|
||||
|
||||
viewModel.myStoreProfile.observe(this) {
|
||||
currentStore = it
|
||||
currentStore = it?.store
|
||||
currentStoreLoaded = true
|
||||
if (storeTypesLoaded) setupStoreTypeSpinner(storeTypesList)
|
||||
updateUI(it)
|
||||
updateUI(it?.store)
|
||||
}
|
||||
|
||||
viewModel.storeTypes.observe(this) {
|
||||
|
||||
@ -10,6 +10,8 @@ import com.alya.ecommerce_serang.data.api.dto.ProductsItem
|
||||
import com.alya.ecommerce_serang.data.api.dto.Store
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.StoreTypesItem
|
||||
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.repository.MyStoreRepository
|
||||
import com.alya.ecommerce_serang.data.repository.Result
|
||||
@ -23,12 +25,18 @@ import java.util.Locale
|
||||
class MyStoreViewModel(private val repository: MyStoreRepository): ViewModel() {
|
||||
private var TAG = "MyStoreViewModel"
|
||||
|
||||
private val _myStoreProfile = MutableLiveData<Store?>()
|
||||
val myStoreProfile: LiveData<Store?> = _myStoreProfile
|
||||
private val _myStoreProfile = MutableLiveData<StoreResponse?>()
|
||||
val myStoreProfile: LiveData<StoreResponse?> = _myStoreProfile
|
||||
|
||||
private val _storeTypes = MutableLiveData<List<StoreTypesItem>>()
|
||||
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>()
|
||||
val isLoadingType: LiveData<Boolean> = _isLoadingType
|
||||
|
||||
@ -47,7 +55,12 @@ class MyStoreViewModel(private val repository: MyStoreRepository): ViewModel() {
|
||||
fun loadMyStore(){
|
||||
viewModelScope.launch {
|
||||
when (val result = repository.fetchMyStoreProfile()){
|
||||
is Result.Success -> _myStoreProfile.postValue(result.data)
|
||||
is Result.Success -> {
|
||||
val storeData = result.data
|
||||
_myStoreProfile.postValue(storeData)
|
||||
_shipping.postValue(storeData?.shipping)
|
||||
_payment.postValue(storeData?.payment)
|
||||
}
|
||||
is Result.Error -> _errorMessage.postValue(result.exception.message ?: "Unknown Error")
|
||||
is Result.Loading -> null
|
||||
}
|
||||
|
||||
@ -7,9 +7,11 @@ import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
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.ResetPassReq
|
||||
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.OtpResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.auth.RegisterResponse
|
||||
@ -388,6 +390,34 @@ class RegisterViewModel(private val repository: UserRepository, private val orde
|
||||
|
||||
}
|
||||
|
||||
fun sendFcm(token: FcmReq) {
|
||||
viewModelScope.launch {
|
||||
_otpState.value = Result.Loading // Indicating API call in progress
|
||||
|
||||
try {
|
||||
// Call the repository function to request OTP
|
||||
val authenticatedApiService = getAuthenticatedApiService()
|
||||
val authenticatedOrderRepo = UserRepository(authenticatedApiService)
|
||||
val response: FcmTokenResponse = authenticatedOrderRepo.sendFcm(token)
|
||||
|
||||
// Log and store success message
|
||||
Log.d("LoginViewModel", "OTP Response: ${response.message}")
|
||||
_message.value = response.message ?: "berhasil" // Store the message for UI feedback
|
||||
|
||||
// Update state to indicate success
|
||||
_otpState.value = Result.Success(Unit)
|
||||
|
||||
} catch (exception: Exception) {
|
||||
// Handle any errors and update state
|
||||
_otpState.value = Result.Error(exception)
|
||||
_message.value = exception.localizedMessage ?: "Failed to request OTP"
|
||||
|
||||
// Log the error for debugging
|
||||
Log.e("LoginViewModel", "OTP request failed for: $token", exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "RegisterViewModel"
|
||||
}
|
||||
|
||||
BIN
app/src/main/res/drawable/ic_change_pass.png
Normal file
BIN
app/src/main/res/drawable/ic_change_pass.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.8 KiB |
102
app/src/main/res/layout/activity_change_password.xml
Normal file
102
app/src/main/res/layout/activity_change_password.xml
Normal file
@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:fitsSystemWindows="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context=".ui.profile.ChangePasswordActivity">
|
||||
|
||||
<include
|
||||
android:id="@+id/headerStoreProduct"
|
||||
layout="@layout/header" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="32dp"
|
||||
android:paddingVertical="16dp">
|
||||
|
||||
<!-- Password label-->
|
||||
<TextView
|
||||
android:id="@+id/tv_password_label"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:text="Kata Sandi Lama"
|
||||
android:textSize="18sp"
|
||||
android:layout_marginVertical="8dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<!-- Password input -->
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/til_login_password"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
app:passwordToggleEnabled="true"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_password_label"
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/et_login_password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Masukkan kata sandi akun"
|
||||
android:inputType="textPassword" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<!-- Password label-->
|
||||
<TextView
|
||||
android:id="@+id/tv_new_password_label"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:text="Kata Sandi Baru"
|
||||
android:textSize="18sp"
|
||||
android:layout_marginVertical="8dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/til_login_password"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<!-- Password input -->
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/til_login_new_password"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
app:passwordToggleEnabled="true"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_new_password_label"
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/et_login_new_password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Masukkan kata sandi baru"
|
||||
android:inputType="textPassword" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<!-- Change Pass button -->
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_change_pass"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Ubah Kata Sandi"
|
||||
app:cornerRadius="8dp"
|
||||
android:layout_marginVertical="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/til_login_new_password" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@ -257,6 +257,15 @@
|
||||
android:textSize="14sp"
|
||||
android:layout_marginBottom="8dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvEmptyPayment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:textSize="12sp"
|
||||
android:text="Pilih alamat terlebih dahulu"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_payment_info"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@ -136,6 +136,7 @@
|
||||
android:id="@+id/tv_registrasi"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:text="@string/signup"
|
||||
android:textColor="@color/blue1"
|
||||
android:textStyle="bold" />
|
||||
|
||||
@ -69,6 +69,35 @@
|
||||
|
||||
</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 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@ -304,6 +304,55 @@
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</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 -->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/card_about"
|
||||
|
||||
@ -50,8 +50,9 @@
|
||||
android:id="@+id/et_otp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Enter OTP"
|
||||
android:hint="Masukkan OTP"
|
||||
android:inputType="number"
|
||||
android:textSize="16dp"
|
||||
android:textAlignment="center"
|
||||
android:maxLength="6" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
@ -61,7 +62,18 @@
|
||||
android:id="@+id/btn_verify"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Verify"
|
||||
android:text="Kirim kode OTP"
|
||||
style="@style/button.large.active.medium"
|
||||
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"
|
||||
android:visibility="gone"
|
||||
style="@style/Widget.Material3.Button.OutlinedButton.Icon"
|
||||
android:text="Kembali"
|
||||
app:cornerRadius="8dp"/>
|
||||
|
||||
<!-- Resend OTP -->
|
||||
@ -81,7 +93,7 @@
|
||||
android:id="@+id/tv_resend_otp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Resend"
|
||||
android:text="Kirim Ulang"
|
||||
android:textColor="@color/blue1"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
@ -233,6 +233,12 @@
|
||||
android:padding="12dp"
|
||||
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) -->
|
||||
<Button
|
||||
android:id="@+id/btn_previous"
|
||||
@ -240,24 +246,14 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:background="@drawable/bg_button_outline"
|
||||
android:visibility="gone"
|
||||
android:text="Kembali"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@color/blue1"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"/>
|
||||
style="@style/Widget.Material3.Button.OutlinedButton.Icon"/>
|
||||
</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>
|
||||
|
||||
<!-- Register Button -->
|
||||
|
||||
Reference in New Issue
Block a user