diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 610dd43..01dfd70 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -29,6 +29,9 @@
android:theme="@style/Theme.Ecommerce_serang"
android:usesCleartextTraffic="true"
tools:targetApi="31">
+
diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/City.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/City.kt
index f8160a2..cb07730 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/City.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/City.kt
@@ -2,30 +2,20 @@ package com.alya.ecommerce_serang.data.api.dto
import com.google.gson.annotations.SerializedName
-data class City(
- @SerializedName("city_id")
- val cityId: String,
+data class CityResponse(
- @SerializedName("city_name")
- val cityName: String,
+ @field:SerializedName("cities")
+ val cities: List,
- @SerializedName("province_id")
- val provinceId: String,
-
- @SerializedName("province")
- val provinceName: String,
-
- @SerializedName("type")
- val type: String,
-
- @SerializedName("postal_code")
- val postalCode: String
+ @field:SerializedName("message")
+ val message: String
)
-data class CityResponse(
- @SerializedName("message")
- val message: String,
+data class City(
- @SerializedName("cities")
- val data: List
-)
\ No newline at end of file
+ @field:SerializedName("city_name")
+ val cityName: String,
+
+ @field:SerializedName("city_id")
+ val cityId: String
+)
diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/ResetPassReq.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/ResetPassReq.kt
new file mode 100644
index 0000000..6d281a6
--- /dev/null
+++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/ResetPassReq.kt
@@ -0,0 +1,8 @@
+package com.alya.ecommerce_serang.data.api.dto
+
+import com.google.gson.annotations.SerializedName
+
+data class ResetPassReq (
+ @SerializedName("emailOrPhone")
+ val emailOrPhone: String
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/UpdateAddressReq.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/UpdateAddressReq.kt
new file mode 100644
index 0000000..68c815f
--- /dev/null
+++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/UpdateAddressReq.kt
@@ -0,0 +1,28 @@
+package com.alya.ecommerce_serang.data.api.dto
+
+import com.google.gson.annotations.SerializedName
+
+data class UpdateAddressReq(
+ @SerializedName("street")
+ val street: String? = "",
+
+ @SerializedName("subdistrict")
+ val subdistrict: String? = "",
+
+ @SerializedName("postal_code")
+ val postalCCode: String? = "",
+
+ @SerializedName("detail")
+ val detail: String? = "",
+
+ @SerializedName("city_id")
+ val cityId: String? = "",
+
+ @SerializedName("province_id")
+ val provId: String? = "",
+
+ @SerializedName("phone")
+ val phone: String? = ""
+
+
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt
index 6a85bca..724db3d 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt
@@ -19,6 +19,7 @@ import com.alya.ecommerce_serang.data.api.dto.OtpRequest
import com.alya.ecommerce_serang.data.api.dto.PaymentConfirmRequest
import com.alya.ecommerce_serang.data.api.dto.ProvinceResponse
import com.alya.ecommerce_serang.data.api.dto.RegisterRequest
+import com.alya.ecommerce_serang.data.api.dto.ResetPassReq
import com.alya.ecommerce_serang.data.api.dto.ReviewProductItem
import com.alya.ecommerce_serang.data.api.dto.SearchRequest
import com.alya.ecommerce_serang.data.api.dto.ShippingServiceRequest
@@ -36,6 +37,7 @@ import com.alya.ecommerce_serang.data.api.response.auth.LoginResponse
import com.alya.ecommerce_serang.data.api.response.auth.OtpResponse
import com.alya.ecommerce_serang.data.api.response.auth.RegisterResponse
import com.alya.ecommerce_serang.data.api.response.auth.RegisterStoreResponse
+import com.alya.ecommerce_serang.data.api.response.auth.ResetPassResponse
import com.alya.ecommerce_serang.data.api.response.auth.VerifRegisterResponse
import com.alya.ecommerce_serang.data.api.response.chat.ChatHistoryResponse
import com.alya.ecommerce_serang.data.api.response.chat.ChatListResponse
@@ -65,6 +67,7 @@ import com.alya.ecommerce_serang.data.api.response.customer.profile.AddressRespo
import com.alya.ecommerce_serang.data.api.response.customer.profile.CreateAddressResponse
import com.alya.ecommerce_serang.data.api.response.customer.profile.EditProfileResponse
import com.alya.ecommerce_serang.data.api.response.customer.profile.ProfileResponse
+import com.alya.ecommerce_serang.data.api.response.customer.profile.UpdateAddressResponse
import com.alya.ecommerce_serang.data.api.response.order.AddEvidenceResponse
import com.alya.ecommerce_serang.data.api.response.order.ComplaintResponse
import com.alya.ecommerce_serang.data.api.response.order.CompletedOrderResponse
@@ -454,6 +457,12 @@ interface ApiService {
@Body addressData: HashMap
): Response
+ @PUT("profile/address/edit/{idAddress}")
+ suspend fun updateAddress(
+ @Path("id") addressId: Int,
+ @Body params: Map
+ ): Response
+
@POST("search")
suspend fun saveSearchQuery(
@Body searchRequest: SearchRequest
@@ -524,4 +533,9 @@ interface ApiService {
suspend fun getVillages(
@Path("subdistrictId") subdistrictId: String
): Response
+
+ @POST("resetpass")
+ suspend fun postResetPass(
+ @Body request: ResetPassReq
+ ): Response
}
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/repository/AddressRepository.kt b/app/src/main/java/com/alya/ecommerce_serang/data/repository/AddressRepository.kt
index 0095e93..3ea4f63 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/data/repository/AddressRepository.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/data/repository/AddressRepository.kt
@@ -1,131 +1,139 @@
package com.alya.ecommerce_serang.data.repository
import android.util.Log
-import com.alya.ecommerce_serang.data.api.dto.City
-import com.alya.ecommerce_serang.data.api.dto.Province
-import com.alya.ecommerce_serang.data.api.dto.StoreAddress
+import com.alya.ecommerce_serang.data.api.dto.CityResponse
+import com.alya.ecommerce_serang.data.api.dto.ProvinceResponse
+import com.alya.ecommerce_serang.data.api.response.customer.profile.AddressResponse
+import com.alya.ecommerce_serang.data.api.response.customer.profile.UpdateAddressResponse
import com.alya.ecommerce_serang.data.api.retrofit.ApiService
-import com.google.gson.Gson
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
-import org.json.JSONObject
+import retrofit2.Response
class AddressRepository(private val apiService: ApiService) {
private val TAG = "AddressRepository"
- suspend fun getProvinces(): List = withContext(Dispatchers.IO) {
- Log.d(TAG, "getProvinces() called")
- try {
- val response = apiService.getProvinces()
- Log.d(TAG, "getProvinces() response: isSuccessful=${response.isSuccessful}, code=${response.code()}")
+// suspend fun getProvinces(): List = withContext(Dispatchers.IO) {
+// Log.d(TAG, "getProvinces() called")
+// try {
+// val response = apiService.getProvinces()
+// Log.d(TAG, "getProvinces() response: isSuccessful=${response.isSuccessful}, code=${response.code()}")
+//
+// // Log the raw response body for debugging
+// val rawBody = response.raw().toString()
+// Log.d(TAG, "Raw response: $rawBody")
+//
+// if (response.isSuccessful) {
+// val responseBody = response.body()
+// Log.d(TAG, "Response body: ${Gson().toJson(responseBody)}")
+//
+// val provinces = responseBody?.data ?: emptyList()
+// Log.d(TAG, "getProvinces() success, got ${provinces.size} provinces")
+// return@withContext provinces
+// } else {
+// val errorBody = response.errorBody()?.string() ?: "Unknown error"
+// Log.e(TAG, "getProvinces() error: $errorBody")
+// throw Exception("API Error (${response.code()}): $errorBody")
+// }
+// } catch (e: Exception) {
+// Log.e(TAG, "Exception in getProvinces()", e)
+// throw Exception("Network error: ${e.message}")
+// }
+// }
- // Log the raw response body for debugging
- val rawBody = response.raw().toString()
- Log.d(TAG, "Raw response: $rawBody")
+// suspend fun getCities(provinceId: String): List = withContext(Dispatchers.IO) {
+// Log.d(TAG, "getCities() called with provinceId: $provinceId")
+// try {
+// val response = apiService.getCities(provinceId)
+// Log.d(TAG, "getCities() response: isSuccessful=${response.isSuccessful}, code=${response.code()}")
+//
+// if (response.isSuccessful) {
+// val responseBody = response.body()
+// Log.d(TAG, "Response body: ${Gson().toJson(responseBody)}")
+//
+// val cities = responseBody?.data ?: emptyList()
+// Log.d(TAG, "getCities() success, got ${cities.size} cities")
+// return@withContext cities
+// } else {
+// val errorBody = response.errorBody()?.string() ?: "Unknown error"
+// Log.e(TAG, "getCities() error: $errorBody")
+// throw Exception("API Error (${response.code()}): $errorBody")
+// }
+// } catch (e: Exception) {
+// Log.e(TAG, "Exception in getCities()", e)
+// throw Exception("Network error: ${e.message}")
+// }
+// }
- if (response.isSuccessful) {
- val responseBody = response.body()
- Log.d(TAG, "Response body: ${Gson().toJson(responseBody)}")
-
- val provinces = responseBody?.data ?: emptyList()
- Log.d(TAG, "getProvinces() success, got ${provinces.size} provinces")
- return@withContext provinces
- } else {
- val errorBody = response.errorBody()?.string() ?: "Unknown error"
- Log.e(TAG, "getProvinces() error: $errorBody")
- throw Exception("API Error (${response.code()}): $errorBody")
- }
- } catch (e: Exception) {
- Log.e(TAG, "Exception in getProvinces()", e)
- throw Exception("Network error: ${e.message}")
- }
+ suspend fun getProvinces(): Response {
+ return apiService.getProvinces()
}
- suspend fun getCities(provinceId: String): List = withContext(Dispatchers.IO) {
- Log.d(TAG, "getCities() called with provinceId: $provinceId")
- try {
- val response = apiService.getCities(provinceId)
- Log.d(TAG, "getCities() response: isSuccessful=${response.isSuccessful}, code=${response.code()}")
-
- if (response.isSuccessful) {
- val responseBody = response.body()
- Log.d(TAG, "Response body: ${Gson().toJson(responseBody)}")
-
- val cities = responseBody?.data ?: emptyList()
- Log.d(TAG, "getCities() success, got ${cities.size} cities")
- return@withContext cities
- } else {
- val errorBody = response.errorBody()?.string() ?: "Unknown error"
- Log.e(TAG, "getCities() error: $errorBody")
- throw Exception("API Error (${response.code()}): $errorBody")
- }
- } catch (e: Exception) {
- Log.e(TAG, "Exception in getCities()", e)
- throw Exception("Network error: ${e.message}")
- }
+ suspend fun getCities(provinceId: String): Response {
+ return apiService.getCities(provinceId)
}
- suspend fun getStoreAddress(): StoreAddress? = withContext(Dispatchers.IO) {
- Log.d(TAG, "getStoreAddress() called")
- try {
- val response = apiService.getStoreAddress()
- Log.d(TAG, "getStoreAddress() response: isSuccessful=${response.isSuccessful}, code=${response.code()}")
-
- if (response.isSuccessful) {
- val responseBody = response.body()
- val rawJson = Gson().toJson(responseBody)
- Log.d(TAG, "Response body: $rawJson")
-
- val address = responseBody?.data
- Log.d(TAG, "getStoreAddress() success, address: $address")
-
- // Convert numeric strings to proper types if needed
- address?.let {
- // Handle city_id if it's a number
- if (it.cityId.isBlank() && rawJson.contains("city_id")) {
- try {
- val cityId = JSONObject(rawJson).getJSONObject("store").optInt("city_id", 0)
- if (cityId > 0) {
- it.javaClass.getDeclaredField("cityId").apply {
- isAccessible = true
- set(it, cityId.toString())
- }
- Log.d(TAG, "Updated cityId to: ${it.cityId}")
- }
- } catch (e: Exception) {
- Log.e(TAG, "Error parsing city_id", e)
- }
- }
-
- // Handle province_id if it's a number
- if (it.provinceId.isBlank() && rawJson.contains("province_id")) {
- try {
- val provinceId = JSONObject(rawJson).getJSONObject("store").optInt("province_id", 0)
- if (provinceId > 0) {
- it.javaClass.getDeclaredField("provinceId").apply {
- isAccessible = true
- set(it, provinceId.toString())
- }
- Log.d(TAG, "Updated provinceId to: ${it.provinceId}")
- }
- } catch (e: Exception) {
- Log.e(TAG, "Error parsing province_id", e)
- }
- }
- }
-
- return@withContext address
- } else {
- val errorBody = response.errorBody()?.string() ?: "Unknown error"
- Log.e(TAG, "getStoreAddress() error: $errorBody")
- throw Exception("API Error (${response.code()}): $errorBody")
- }
- } catch (e: Exception) {
- Log.e(TAG, "Exception in getStoreAddress()", e)
- throw Exception("Network error: ${e.message}")
- }
- }
+// suspend fun getStoreAddress(): StoreAddress? = withContext(Dispatchers.IO) {
+// Log.d(TAG, "getStoreAddress() called")
+// try {
+// val response = apiService.getStoreAddress()
+// Log.d(TAG, "getStoreAddress() response: isSuccessful=${response.isSuccessful}, code=${response.code()}")
+//
+// if (response.isSuccessful) {
+// val responseBody = response.body()
+// val rawJson = Gson().toJson(responseBody)
+// Log.d(TAG, "Response body: $rawJson")
+//
+// val address = responseBody?.data
+// Log.d(TAG, "getStoreAddress() success, address: $address")
+//
+// // Convert numeric strings to proper types if needed
+// address?.let {
+// // Handle city_id if it's a number
+// if (it.cityId.isBlank() && rawJson.contains("city_id")) {
+// try {
+// val cityId = JSONObject(rawJson).getJSONObject("store").optInt("city_id", 0)
+// if (cityId > 0) {
+// it.javaClass.getDeclaredField("cityId").apply {
+// isAccessible = true
+// set(it, cityId.toString())
+// }
+// Log.d(TAG, "Updated cityId to: ${it.cityId}")
+// }
+// } catch (e: Exception) {
+// Log.e(TAG, "Error parsing city_id", e)
+// }
+// }
+//
+// // Handle province_id if it's a number
+// if (it.provinceId.isBlank() && rawJson.contains("province_id")) {
+// try {
+// val provinceId = JSONObject(rawJson).getJSONObject("store").optInt("province_id", 0)
+// if (provinceId > 0) {
+// it.javaClass.getDeclaredField("provinceId").apply {
+// isAccessible = true
+// set(it, provinceId.toString())
+// }
+// Log.d(TAG, "Updated provinceId to: ${it.provinceId}")
+// }
+// } catch (e: Exception) {
+// Log.e(TAG, "Error parsing province_id", e)
+// }
+// }
+// }
+//
+// return@withContext address
+// } else {
+// val errorBody = response.errorBody()?.string() ?: "Unknown error"
+// Log.e(TAG, "getStoreAddress() error: $errorBody")
+// throw Exception("API Error (${response.code()}): $errorBody")
+// }
+// } catch (e: Exception) {
+// Log.e(TAG, "Exception in getStoreAddress()", e)
+// throw Exception("Network error: ${e.message}")
+// }
+// }
suspend fun saveStoreAddress(
provinceId: String,
@@ -171,4 +179,12 @@ class AddressRepository(private val apiService: ApiService) {
throw Exception("Network error: ${e.message}")
}
}
+
+ suspend fun getStoreAddress(): Response {
+ return apiService.getAddress()
+ }
+
+ suspend fun updateAddress(addressId: Int, params: Map): Response {
+ return apiService.updateAddress(addressId, params)
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/repository/UserRepository.kt b/app/src/main/java/com/alya/ecommerce_serang/data/repository/UserRepository.kt
index c6acff8..bf7f317 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/data/repository/UserRepository.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/data/repository/UserRepository.kt
@@ -7,6 +7,7 @@ import com.alya.ecommerce_serang.data.api.dto.FcmReq
import com.alya.ecommerce_serang.data.api.dto.LoginRequest
import com.alya.ecommerce_serang.data.api.dto.OtpRequest
import com.alya.ecommerce_serang.data.api.dto.RegisterRequest
+import com.alya.ecommerce_serang.data.api.dto.ResetPassReq
import com.alya.ecommerce_serang.data.api.dto.UserProfile
import com.alya.ecommerce_serang.data.api.dto.VerifRegisReq
import com.alya.ecommerce_serang.data.api.response.auth.FcmTokenResponse
@@ -18,6 +19,7 @@ import com.alya.ecommerce_serang.data.api.response.auth.NotifstoreItem
import com.alya.ecommerce_serang.data.api.response.auth.OtpResponse
import com.alya.ecommerce_serang.data.api.response.auth.RegisterResponse
import com.alya.ecommerce_serang.data.api.response.auth.RegisterStoreResponse
+import com.alya.ecommerce_serang.data.api.response.auth.ResetPassResponse
import com.alya.ecommerce_serang.data.api.response.auth.VerifRegisterResponse
import com.alya.ecommerce_serang.data.api.response.customer.order.ListCityResponse
import com.alya.ecommerce_serang.data.api.response.customer.order.ListProvinceResponse
@@ -490,6 +492,30 @@ class UserRepository(private val apiService: ApiService) {
Result.Error(e)
}
}
+
+ suspend fun resetPassword(request: ResetPassReq): Result{
+ return try {
+ val response = apiService.postResetPass(request)
+
+ if (response.isSuccessful){
+ val resetPassResponse = response.body()
+ if (resetPassResponse != null) {
+ Result.Success(resetPassResponse)
+ }
+ else {
+ Result.Error(Exception("Empty response from server"))
+ }
+ }
+ else {
+ val errorBody = response.errorBody()?.string() ?: "Unknown error"
+ Log.e(TAG, "Error RESET PASS address: $errorBody")
+ Result.Error(Exception(errorBody))
+ }
+ }
+ catch (e: Exception){
+ Result.Error(e)
+ }
+ }
companion object{
private const val TAG = "UserRepository"
}
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/auth/LoginActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/auth/LoginActivity.kt
index 01e7307..9d8184c 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/ui/auth/LoginActivity.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/auth/LoginActivity.kt
@@ -82,6 +82,11 @@ class LoginActivity : AppCompatActivity() {
startActivity(Intent(this, RegisterActivity::class.java))
finish()
}
+
+ binding.tvForgetPassword.setOnClickListener {
+ startActivity(Intent(this, ResetPassActivity::class.java))
+ finish()
+ }
}
private fun observeLoginState() {
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/auth/ResetPassActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/auth/ResetPassActivity.kt
new file mode 100644
index 0000000..70cc88d
--- /dev/null
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/auth/ResetPassActivity.kt
@@ -0,0 +1,123 @@
+package com.alya.ecommerce_serang.ui.auth
+
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import android.widget.Toast
+import androidx.activity.viewModels
+import androidx.appcompat.app.AlertDialog
+import androidx.appcompat.app.AppCompatActivity
+import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
+import com.alya.ecommerce_serang.data.repository.Result
+import com.alya.ecommerce_serang.data.repository.UserRepository
+import com.alya.ecommerce_serang.databinding.ActivityResetPassBinding
+import com.alya.ecommerce_serang.utils.BaseViewModelFactory
+import com.alya.ecommerce_serang.utils.viewmodel.LoginViewModel
+
+class ResetPassActivity : AppCompatActivity() {
+
+ private val TAG = "ResetPassActivity"
+ private lateinit var binding: ActivityResetPassBinding
+
+ private val loginViewModel: LoginViewModel by viewModels{
+ BaseViewModelFactory {
+ val apiService = ApiConfig.getUnauthenticatedApiService()
+ val userRepository = UserRepository(apiService)
+ LoginViewModel(userRepository, this)
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ binding = ActivityResetPassBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+// enableEdgeToEdge()
+
+ setupToolbar()
+ setupUI()
+
+ }
+
+ private fun setupToolbar(){
+ binding.headerResetPass.headerLeftIcon.setOnClickListener{
+ finish()
+ }
+ binding.headerResetPass.headerTitle.text = "Lupa Password"
+ }
+
+ private fun setupUI(){
+ binding.btnReset.setOnClickListener {
+ val email = binding.etEmail.text.toString().trim()
+ if (email.isNotEmpty()) {
+ loginViewModel.resetPassword(email)
+ } else {
+ binding.etEmail.error = "Masukkan Email Anda"
+ }
+ }
+ }
+
+ private fun observeResetPassword() {
+ loginViewModel.resetPasswordState.observe(this) { result ->
+ when (result) {
+ is com.alya.ecommerce_serang.data.repository.Result.Loading -> {
+ showLoading(true)
+ }
+
+ is com.alya.ecommerce_serang.data.repository.Result.Success -> {
+ showLoading(false)
+ handleSuccess("Silahkan cek email anda untuk melihat password anda.")
+ Log.d(TAG, "Success rest password: ${result.data.message}")
+ }
+
+ is Result.Error -> {
+ showLoading(false)
+ handleError("Email anda salah atau tidak ditemukan.")
+ Log.e(TAG, "Error reset password ${result.exception.message}")
+ }
+
+ null -> {
+ // Initial state
+ }
+ }
+ }
+ }
+
+ private fun showLoading(isLoading: Boolean) {
+ if (isLoading) {
+ binding.progressBar.visibility = View.VISIBLE
+ binding.btnReset.isEnabled = false
+ binding.etEmail.isEnabled = false
+ } else {
+ binding.progressBar.visibility = View.GONE
+ binding.btnReset.isEnabled = true
+ binding.etEmail.isEnabled = true
+ }
+ }
+
+ private fun handleSuccess(message: String) {
+ Toast.makeText(this, message, Toast.LENGTH_LONG).show()
+
+ // Show success dialog and navigate back to login
+ AlertDialog.Builder(this)
+ .setTitle("Berhasil Ubah Password")
+ .setMessage(message)
+ .setPositiveButton("OK") { _, _ ->
+ // Navigate back to login activity
+ finish()
+ }
+ .setCancelable(false)
+ .show()
+ }
+
+ private fun handleError(errorMessage: String) {
+ Toast.makeText(this, "Error: $errorMessage", Toast.LENGTH_LONG).show()
+
+ // Optionally show error dialog
+ AlertDialog.Builder(this)
+ .setTitle("Gagal Ubah Password")
+ .setMessage(errorMessage)
+ .setPositiveButton("OK", null)
+ .show()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/order/address/AddressActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/order/address/AddressActivity.kt
index edd0a5a..3c7dcaa 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/ui/order/address/AddressActivity.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/order/address/AddressActivity.kt
@@ -99,6 +99,7 @@ class AddressActivity : AppCompatActivity() {
viewModel.selectedAddressId.observe(this) { selectedId ->
adapter.setSelectedAddressId(selectedId)
+ finish()
}
}
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/address/DetailStoreAddressActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/address/DetailStoreAddressActivity.kt
index c90fe8b..26bfc87 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/address/DetailStoreAddressActivity.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/address/DetailStoreAddressActivity.kt
@@ -1,6 +1,5 @@
package com.alya.ecommerce_serang.ui.profile.mystore.profile.address
-import android.app.Activity
import android.os.Bundle
import android.util.Log
import android.view.View
@@ -14,13 +13,14 @@ import com.alya.ecommerce_serang.BuildConfig
import com.alya.ecommerce_serang.R
import com.alya.ecommerce_serang.data.api.dto.City
import com.alya.ecommerce_serang.data.api.dto.Province
+import com.alya.ecommerce_serang.data.api.response.customer.profile.AddressesItem
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.AddressRepository
import com.alya.ecommerce_serang.databinding.ActivityDetailStoreAddressBinding
-import com.alya.ecommerce_serang.utils.viewmodel.AddressViewModel
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
import com.alya.ecommerce_serang.utils.SessionManager
+import com.alya.ecommerce_serang.utils.viewmodel.AddressViewModel
import com.google.android.material.snackbar.Snackbar
class DetailStoreAddressActivity : AppCompatActivity() {
@@ -32,6 +32,7 @@ class DetailStoreAddressActivity : AppCompatActivity() {
private var selectedCityId: String? = null
private var provinces: List = emptyList()
private var cities: List = emptyList()
+ private var currentAddress: AddressesItem? = null
private val TAG = "StoreAddressActivity"
@@ -178,6 +179,7 @@ class DetailStoreAddressActivity : AppCompatActivity() {
// Observe store address data
viewModel.storeAddress.observe(this) { address ->
+ currentAddress = address
Log.d(TAG, "Received store address: $address")
address?.let {
// Set the fields
@@ -214,11 +216,12 @@ class DetailStoreAddressActivity : AppCompatActivity() {
}
// Observe save success
- viewModel.saveSuccess.observe(this) {
- if (it) {
- Toast.makeText(this, "Alamat berhasil disimpan", Toast.LENGTH_SHORT).show()
- setResult(Activity.RESULT_OK)
+ viewModel.saveSuccess.observe(this) { success ->
+ if (success) {
+ Log.d(TAG, "Address updated successfully")
finish()
+ } else {
+ Log.e(TAG, "Failed to update address")
}
}
}
@@ -229,8 +232,8 @@ class DetailStoreAddressActivity : AppCompatActivity() {
val subdistrict = binding.edtSubdistrict.text.toString()
val detail = binding.edtDetailAddress.text.toString()
val postalCode = binding.edtPostalCode.text.toString()
- val latitude = binding.edtLatitude.text.toString().toDoubleOrNull() ?: 0.0
- val longitude = binding.edtLongitude.text.toString().toDoubleOrNull() ?: 0.0
+ val latitude = binding.edtLatitude.text.toString()
+ val longitude = binding.edtLongitude.text.toString()
val city = cities.find { it.cityId == selectedCityId }
val province = provinces.find { it.provinceId == selectedProvinceId }
@@ -241,12 +244,10 @@ class DetailStoreAddressActivity : AppCompatActivity() {
return@setOnClickListener
}
- // Save address
- viewModel.saveStoreAddress(
+ val oldAddress = currentAddress ?: return@setOnClickListener
+ val newAddress = oldAddress.copy(
provinceId = selectedProvinceId!!,
- provinceName = province?.provinceName ?: "",
cityId = city.cityId,
- cityName = city.cityName,
street = street,
subdistrict = subdistrict,
detail = detail,
@@ -254,6 +255,20 @@ class DetailStoreAddressActivity : AppCompatActivity() {
latitude = latitude,
longitude = longitude
)
+ viewModel.saveStoreAddress(oldAddress, newAddress)
+ // Save address
+// viewModel.saveStoreAddress(
+// provinceId = selectedProvinceId!!,
+// provinceName = province?.provinceName ?: "",
+// cityId = city.cityId,
+// cityName = city.cityName,
+// street = street,
+// subdistrict = subdistrict,
+// detail = detail,
+// postalCode = postalCode,
+// latitude = latitude,
+// longitude = longitude
+// )
}
}
diff --git a/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/AddressViewModel.kt b/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/AddressViewModel.kt
index 9f3d94a..3f3bb5c 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/AddressViewModel.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/AddressViewModel.kt
@@ -7,7 +7,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.alya.ecommerce_serang.data.api.dto.City
import com.alya.ecommerce_serang.data.api.dto.Province
-import com.alya.ecommerce_serang.data.api.dto.StoreAddress
+import com.alya.ecommerce_serang.data.api.response.customer.profile.AddressesItem
import com.alya.ecommerce_serang.data.repository.AddressRepository
import kotlinx.coroutines.launch
@@ -21,8 +21,8 @@ class AddressViewModel(private val addressRepository: AddressRepository) : ViewM
private val _cities = MutableLiveData>()
val cities: LiveData> = _cities
- private val _storeAddress = MutableLiveData()
- val storeAddress: LiveData = _storeAddress
+ private val _storeAddress = MutableLiveData()
+ val storeAddress: LiveData get() = _storeAddress
private val _isLoading = MutableLiveData()
val isLoading: LiveData = _isLoading
@@ -31,99 +31,219 @@ class AddressViewModel(private val addressRepository: AddressRepository) : ViewM
val errorMessage: LiveData = _errorMessage
private val _saveSuccess = MutableLiveData()
- val saveSuccess: LiveData = _saveSuccess
+ val saveSuccess: LiveData get() = _saveSuccess
+
fun fetchProvinces() {
- Log.d(TAG, "fetchProvinces() called")
- _isLoading.value = true
viewModelScope.launch {
try {
- Log.d(TAG, "Calling addressRepository.getProvinces()")
val response = addressRepository.getProvinces()
- Log.d(TAG, "Received provinces response: ${response.size} provinces")
- _provinces.value = response
- _isLoading.value = false
+ if (response.isSuccessful) {
+ _provinces.value = response.body()?.data ?: emptyList()
+ } else {
+ Log.e("EditAddressVM", "Failed to get provinces: ${response.message()}")
+ }
} catch (e: Exception) {
- Log.e(TAG, "Error fetching provinces", e)
- _errorMessage.value = "Failed to load provinces: ${e.message}"
- _isLoading.value = false
+ Log.e("EditAddressVM", "Error getting provinces: ${e.message}")
}
}
}
fun fetchCities(provinceId: String) {
- Log.d(TAG, "fetchCities() called with provinceId: $provinceId")
- _isLoading.value = true
viewModelScope.launch {
try {
- Log.d(TAG, "Calling addressRepository.getCities()")
val response = addressRepository.getCities(provinceId)
- Log.d(TAG, "Received cities response: ${response.size} cities")
- _cities.value = response
- _isLoading.value = false
+ if (response.isSuccessful) {
+ _cities.value = response.body()?.cities ?: emptyList()
+ } else {
+ Log.e("EditAddressVM", "Failed to get cities: ${response.message()}")
+ }
} catch (e: Exception) {
- Log.e(TAG, "Error fetching cities", e)
- _errorMessage.value = "Failed to load cities: ${e.message}"
- _isLoading.value = false
+ Log.e("EditAddressVM", "Error getting cities: ${e.message}")
}
}
}
+// fun fetchProvinces() {
+// Log.d(TAG, "fetchProvinces() called")
+// _isLoading.value = true
+// viewModelScope.launch {
+// try {
+// Log.d(TAG, "Calling addressRepository.getProvinces()")
+// val response = addressRepository.getProvinces()
+// Log.d(TAG, "Received provinces response: ${response.size} provinces")
+// _provinces.value = response
+// _isLoading.value = false
+// } catch (e: Exception) {
+// Log.e(TAG, "Error fetching provinces", e)
+// _errorMessage.value = "Failed to load provinces: ${e.message}"
+// _isLoading.value = false
+// }
+// }
+// }
+
+// fun fetchCities(provinceId: String) {
+// Log.d(TAG, "fetchCities() called with provinceId: $provinceId")
+// _isLoading.value = true
+// viewModelScope.launch {
+// try {
+// selecte
+// Log.d(TAG, "Calling addressRepository.getCities()")
+// val response = addressRepository.getCities(provinceId)
+// Log.d(TAG, "Received cities response: ${response.size} cities")
+// _cities.value = response
+// _isLoading.value = false
+// } catch (e: Exception) {
+// Log.e(TAG, "Error fetching cities", e)
+// _errorMessage.value = "Failed to load cities: ${e.message}"
+// _isLoading.value = false
+// }
+// }
+// }
+
+// fun fetchStoreAddress() {
+// Log.d(TAG, "fetchStoreAddress() called")
+// _isLoading.value = true
+// viewModelScope.launch {
+// try {
+// Log.d(TAG, "Calling addressRepository.getStoreAddress()")
+// val response = addressRepository.getStoreAddress()
+// Log.d(TAG, "Received store address response: $response")
+// _storeAddress.value = response
+// _isLoading.value = false
+// } catch (e: Exception) {
+// Log.e(TAG, "Error fetching store address", e)
+// _errorMessage.value = "Failed to load store address: ${e.message}"
+// _isLoading.value = false
+// }
+// }
+// }
+
+// fun fetchStoreAddress() {
+// viewModelScope.launch {
+// try {
+// val response = addressRepository.getStoreAddress()
+// if (response.isSuccessful) {
+// val storeAddress = response.body()?.addresses
+// ?.firstOrNull { it.isStoreLocation == true }
+//
+// if (storeAddress != null) {
+// _storeAddress.value = storeAddress
+// } else {
+// Log.d("EditAddressVM", "No store address found")
+// }
+// } else {
+// Log.e("EditAddressVM", "Failed to get addresses: ${response.message()}")
+// }
+// } catch (e: Exception) {
+// Log.e("EditAddressVM", "Error: ${e.message}")
+// }
+// }
+// }
+
fun fetchStoreAddress() {
- Log.d(TAG, "fetchStoreAddress() called")
- _isLoading.value = true
viewModelScope.launch {
try {
- Log.d(TAG, "Calling addressRepository.getStoreAddress()")
val response = addressRepository.getStoreAddress()
- Log.d(TAG, "Received store address response: $response")
- _storeAddress.value = response
- _isLoading.value = false
+ if (response.isSuccessful) {
+ val storeAddress = response.body()?.addresses
+ ?.firstOrNull { it.isStoreLocation == true }
+
+ if (storeAddress != null) {
+ _storeAddress.value = storeAddress
+ } else {
+ Log.d(TAG, "No store address found")
+ }
+ } else {
+ Log.e(TAG, "Failed to get addresses: ${response.message()}")
+ }
} catch (e: Exception) {
- Log.e(TAG, "Error fetching store address", e)
- _errorMessage.value = "Failed to load store address: ${e.message}"
- _isLoading.value = false
+ Log.e(TAG, "Error: ${e.message}")
}
}
}
- fun saveStoreAddress(
- provinceId: String,
- provinceName: String,
- cityId: String,
- cityName: String,
- street: String,
- subdistrict: String,
- detail: String,
- postalCode: String,
- latitude: Double,
- longitude: Double
- ) {
- Log.d(TAG, "saveStoreAddress() called with provinceId: $provinceId, cityId: $cityId")
- _isLoading.value = true
+// fun saveStoreAddress(
+// provinceId: String,
+// provinceName: String,
+// cityId: String,
+// cityName: String,
+// street: String,
+// subdistrict: String,
+// detail: String,
+// postalCode: String,
+// latitude: Double,
+// longitude: Double
+// ) {
+// Log.d(TAG, "saveStoreAddress() called with provinceId: $provinceId, cityId: $cityId")
+// _isLoading.value = true
+// viewModelScope.launch {
+// try {
+// Log.d(TAG, "Calling addressRepository.saveStoreAddress()")
+// val success = addressRepository.saveStoreAddress(
+// provinceId = provinceId,
+// provinceName = provinceName,
+// cityId = cityId,
+// cityName = cityName,
+// street = street,
+// subdistrict = subdistrict,
+// detail = detail,
+// postalCode = postalCode,
+// latitude = latitude,
+// longitude = longitude
+// )
+// Log.d(TAG, "Save store address result: $success")
+// _saveSuccess.value = success
+// _isLoading.value = false
+// } catch (e: Exception) {
+// Log.e(TAG, "Error saving store address", e)
+// _errorMessage.value = "Failed to save address: ${e.message}"
+// _isLoading.value = false
+// }
+// }
+// }
+
+ fun saveStoreAddress(oldAddress: AddressesItem, newAddress: AddressesItem) {
+ val params = buildUpdateBody(oldAddress, newAddress)
+ if (params.isEmpty()) {
+ Log.d(TAG, "No changes detected")
+ _saveSuccess.value = false
+ return
+ }
+
viewModelScope.launch {
try {
- Log.d(TAG, "Calling addressRepository.saveStoreAddress()")
- val success = addressRepository.saveStoreAddress(
- provinceId = provinceId,
- provinceName = provinceName,
- cityId = cityId,
- cityName = cityName,
- street = street,
- subdistrict = subdistrict,
- detail = detail,
- postalCode = postalCode,
- latitude = latitude,
- longitude = longitude
- )
- Log.d(TAG, "Save store address result: $success")
- _saveSuccess.value = success
- _isLoading.value = false
+ val response = addressRepository.updateAddress(oldAddress.id, params)
+ _saveSuccess.value = response.isSuccessful
} catch (e: Exception) {
- Log.e(TAG, "Error saving store address", e)
- _errorMessage.value = "Failed to save address: ${e.message}"
- _isLoading.value = false
+ Log.e(TAG, "Error: ${e.message}")
+ _saveSuccess.value = false
}
}
}
+
+ private fun buildUpdateBody(oldAddress: AddressesItem, newAddress: AddressesItem): Map {
+ val params = mutableMapOf()
+
+ fun addIfChanged(key: String, oldValue: Any?, newValue: Any?) {
+ if (newValue != null && newValue != oldValue) {
+ params[key] = newValue
+ }
+ }
+
+ addIfChanged("street", oldAddress.street, newAddress.street)
+ addIfChanged("province_id", oldAddress.provinceId, newAddress.provinceId)
+ addIfChanged("detail", oldAddress.detail, newAddress.detail)
+ addIfChanged("subdistrict", oldAddress.subdistrict, newAddress.subdistrict)
+ addIfChanged("city_id", oldAddress.cityId, newAddress.cityId)
+ addIfChanged("village_id", oldAddress.villageId, newAddress.villageId)
+ addIfChanged("postal_code", oldAddress.postalCode, newAddress.postalCode)
+ addIfChanged("phone", oldAddress.phone, newAddress.phone)
+ addIfChanged("recipient", oldAddress.recipient, newAddress.recipient)
+ addIfChanged("latitude", oldAddress.latitude, newAddress.latitude)
+ addIfChanged("longitude", oldAddress.longitude, newAddress.longitude)
+ addIfChanged("is_store_location", oldAddress.isStoreLocation, newAddress.isStoreLocation)
+
+ return params
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/LoginViewModel.kt b/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/LoginViewModel.kt
index e4a6592..934cd71 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/LoginViewModel.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/LoginViewModel.kt
@@ -7,8 +7,10 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.alya.ecommerce_serang.data.api.dto.FcmReq
+import com.alya.ecommerce_serang.data.api.dto.ResetPassReq
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.ResetPassResponse
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
@@ -27,6 +29,10 @@ class LoginViewModel(private val repository: UserRepository, private val context
private val _message = MutableLiveData()
val message: LiveData = _message
+ private val _resetPasswordState = MutableLiveData?>()
+ val resetPasswordState: LiveData?> = _resetPasswordState
+
+
private val sessionManager by lazy { SessionManager(context) }
private fun getAuthenticatedApiService(): ApiService {
@@ -69,4 +75,19 @@ class LoginViewModel(private val repository: UserRepository, private val context
}
}
+ fun resetPassword(email: String) {
+ viewModelScope.launch {
+ _resetPasswordState.value = Result.Loading
+
+ val request = ResetPassReq(emailOrPhone = email)
+ val result = repository.resetPassword(request)
+
+ _resetPasswordState.value = result
+ }
+ }
+
+ fun clearState() {
+ _resetPasswordState.value = null
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/RegisterViewModel.kt b/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/RegisterViewModel.kt
index 7cddac2..07a062b 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/RegisterViewModel.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/RegisterViewModel.kt
@@ -8,6 +8,7 @@ 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.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.LoginResponse
import com.alya.ecommerce_serang.data.api.response.auth.OtpResponse
@@ -382,6 +383,10 @@ class RegisterViewModel(private val repository: UserRepository, private val orde
}
}
+ fun resetPass(request: ResetPassReq){
+
+ }
+
companion object {
private const val TAG = "RegisterViewModel"
}
diff --git a/app/src/main/res/layout/activity_detail_store_address.xml b/app/src/main/res/layout/activity_detail_store_address.xml
index 230a417..caf9113 100644
--- a/app/src/main/res/layout/activity_detail_store_address.xml
+++ b/app/src/main/res/layout/activity_detail_store_address.xml
@@ -301,7 +301,8 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
- android:layout_marginEnd="8dp">
+ android:layout_marginEnd="8dp"
+ android:visibility="gone">
+ android:layout_marginStart="8dp"
+ android:visibility="gone">
diff --git a/app/src/main/res/layout/activity_reset_pass.xml b/app/src/main/res/layout/activity_reset_pass.xml
new file mode 100644
index 0000000..1f34826
--- /dev/null
+++ b/app/src/main/res/layout/activity_reset_pass.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_register_step3.xml b/app/src/main/res/layout/fragment_register_step3.xml
index f00a00d..779f947 100644
--- a/app/src/main/res/layout/fragment_register_step3.xml
+++ b/app/src/main/res/layout/fragment_register_step3.xml
@@ -245,6 +245,19 @@
android:textColor="@color/blue1"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"/>
+
+
+
+
+
+
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index e09c11f..7ee4510 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,5 +1,5 @@
[versions]
-agp = "8.12.0"
+agp = "8.9.2"
glide = "4.16.0"
gson = "2.11.0"
hiltAndroid = "2.56.2" # Updated from 2.44 for better compatibility