mirror of
https://github.com/shaulascr/ecommerce_serang.git
synced 2025-08-10 17:32:22 +00:00
update address
This commit is contained in:
@ -4,6 +4,8 @@
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
@ -21,7 +21,7 @@ data class CreateAddressRequest (
|
||||
@SerializedName("province_id")
|
||||
val provId: Int,
|
||||
@SerializedName("postal_code")
|
||||
val postCode: String,
|
||||
val postCode: String? = null,
|
||||
|
||||
@SerializedName("detail")
|
||||
val detailAddress: String? = null,
|
||||
|
@ -0,0 +1,11 @@
|
||||
package com.alya.ecommerce_serang.data.api.dto
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class UpdateCart (
|
||||
@SerializedName("cart_item_id")
|
||||
val cartItemId: Int,
|
||||
|
||||
@SerializedName("quantity")
|
||||
val quantity: Int
|
||||
)
|
@ -26,7 +26,7 @@ data class AddressesItem(
|
||||
val provinceId: Int,
|
||||
|
||||
@field:SerializedName("phone")
|
||||
val phone: Any,
|
||||
val phone: String,
|
||||
|
||||
@field:SerializedName("street")
|
||||
val street: String,
|
||||
@ -35,7 +35,7 @@ data class AddressesItem(
|
||||
val subdistrict: String,
|
||||
|
||||
@field:SerializedName("recipient")
|
||||
val recipient: Any,
|
||||
val recipient: String,
|
||||
|
||||
@field:SerializedName("id")
|
||||
val id: Int,
|
||||
|
@ -7,6 +7,7 @@ import com.alya.ecommerce_serang.data.api.dto.LoginRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.OrderRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.OtpRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.RegisterRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.UpdateCart
|
||||
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
|
||||
@ -14,6 +15,9 @@ import com.alya.ecommerce_serang.data.api.response.cart.AddCartResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.cart.ListCartResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.order.CourierCostResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.order.CreateOrderResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.order.ListCityResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.order.ListProvinceResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.order.UpdateCartResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.product.AllProductResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.product.CategoryResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.product.DetailStoreProductResponse
|
||||
@ -21,11 +25,13 @@ import com.alya.ecommerce_serang.data.api.response.product.ProductResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.product.ReviewProductResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.product.StoreResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.profile.AddressResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.profile.CreateAddressResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.profile.ProfileResponse
|
||||
import retrofit2.Response
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.PUT
|
||||
import retrofit2.http.Path
|
||||
|
||||
interface ApiService {
|
||||
@ -80,9 +86,8 @@ interface ApiService {
|
||||
|
||||
@POST("profile/addaddress")
|
||||
suspend fun createAddress(
|
||||
@Body addressRequest: CreateAddressRequest
|
||||
): Response<AddressResponse>
|
||||
|
||||
@Body createAddressRequest: CreateAddressRequest
|
||||
): Response<CreateAddressResponse>
|
||||
|
||||
@GET("mystore")
|
||||
suspend fun getStore (): Response<StoreResponse>
|
||||
@ -95,8 +100,23 @@ interface ApiService {
|
||||
@Body cartRequest: CartItem
|
||||
): Response<AddCartResponse>
|
||||
|
||||
@PUT("cart/update")
|
||||
suspend fun updateCart(
|
||||
@Body updateCart: UpdateCart
|
||||
): Response<UpdateCartResponse>
|
||||
|
||||
@POST("couriercost")
|
||||
suspend fun countCourierCost(
|
||||
@Body courierCost : CourierCostRequest
|
||||
): CourierCostResponse
|
||||
): Response<CourierCostResponse>
|
||||
|
||||
@GET("cities/{id}")
|
||||
suspend fun getCityProvId(
|
||||
@Path("id") provId : Int
|
||||
): Response<ListCityResponse>
|
||||
|
||||
@GET("provinces")
|
||||
suspend fun getListProv(
|
||||
): Response<ListProvinceResponse>
|
||||
|
||||
}
|
@ -1,10 +1,14 @@
|
||||
package com.alya.ecommerce_serang.data.repository
|
||||
|
||||
import android.util.Log
|
||||
import com.alya.ecommerce_serang.data.api.dto.CreateAddressRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.OrderRequest
|
||||
import com.alya.ecommerce_serang.data.api.response.order.CreateOrderResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.order.ListCityResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.order.ListProvinceResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.product.ProductResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.product.StoreResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.profile.CreateAddressResponse
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiService
|
||||
import retrofit2.Response
|
||||
|
||||
@ -33,17 +37,31 @@ class OrderRepository(private val apiService: ApiService) {
|
||||
return if (response.isSuccessful) response.body() else null
|
||||
}
|
||||
|
||||
//post data with message/response
|
||||
suspend fun addAddress(createAddressRequest: CreateAddressRequest): Result<CreateAddressResponse> {
|
||||
return try {
|
||||
val response = apiService.createAddress(createAddressRequest)
|
||||
if (response.isSuccessful){
|
||||
response.body()?.let {
|
||||
Result.Success(it)
|
||||
} ?: Result.Error(Exception("Add Address failed"))
|
||||
} else {
|
||||
Log.e("OrderRepository", "Error: ${response.errorBody()?.string()}")
|
||||
Result.Error(Exception(response.errorBody()?.string() ?: "Unknown error"))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Result.Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
//not yet implement the api service address
|
||||
// suspend fun getAddressDetails(addressId: Int): AddressesItem {
|
||||
// // Simulate API call to get address details
|
||||
// kotlinx.coroutines.delay(300) // Simulate network request
|
||||
// // Return mock data
|
||||
// return AddressesItem(
|
||||
// id = addressId,
|
||||
// label = "Rumah",
|
||||
// fullAddress = "Jl. Pegangasan Timur No. 42, Jakarta"
|
||||
// )
|
||||
// }
|
||||
suspend fun getListProvinces(): ListProvinceResponse? {
|
||||
val response = apiService.getListProv()
|
||||
return if (response.isSuccessful) response.body() else null
|
||||
}
|
||||
|
||||
suspend fun getListCities(provId : Int): ListCityResponse?{
|
||||
val response = apiService.getCityProvId(provId)
|
||||
return if (response.isSuccessful) response.body() else null
|
||||
}
|
||||
|
||||
}
|
@ -10,12 +10,9 @@ import com.alya.ecommerce_serang.data.api.retrofit.ApiService
|
||||
|
||||
class UserRepository(private val apiService: ApiService) {
|
||||
|
||||
//post data without message/response
|
||||
suspend fun requestOtpRep(email: String): OtpResponse {
|
||||
|
||||
// fun requestOtpRep(email: String): Result<String> {
|
||||
|
||||
return apiService.getOTP(OtpRequest(email))
|
||||
|
||||
}
|
||||
|
||||
suspend fun registerUser(request: RegisterRequest): String {
|
||||
|
@ -1,21 +1,289 @@
|
||||
package com.alya.ecommerce_serang.ui.order
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.pm.PackageManager
|
||||
import android.location.Location
|
||||
import android.location.LocationListener
|
||||
import android.location.LocationManager
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.alya.ecommerce_serang.R
|
||||
import androidx.constraintlayout.motion.widget.Debug.getLocation
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.alya.ecommerce_serang.data.api.dto.CreateAddressRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.UserProfile
|
||||
import com.alya.ecommerce_serang.data.api.response.order.CitiesItem
|
||||
import com.alya.ecommerce_serang.data.api.response.order.ProvincesItem
|
||||
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.OrderRepository
|
||||
import com.alya.ecommerce_serang.databinding.ActivityAddAddressBinding
|
||||
import com.alya.ecommerce_serang.utils.SavedStateViewModelFactory
|
||||
import com.alya.ecommerce_serang.utils.SessionManager
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class AddAddressActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContentView(R.layout.activity_add_address)
|
||||
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
|
||||
private lateinit var binding: ActivityAddAddressBinding
|
||||
private lateinit var apiService: ApiService
|
||||
private lateinit var sessionManager: SessionManager
|
||||
private lateinit var profileUser: UserProfile
|
||||
private lateinit var locationManager: LocationManager
|
||||
|
||||
private var latitude: Double? = null
|
||||
private var longitude: Double? = null
|
||||
private val provinceAdapter by lazy { ProvinceAdapter(this) }
|
||||
private val cityAdapter by lazy { CityAdapter(this) }
|
||||
|
||||
private val viewModel: AddAddressViewModel by viewModels {
|
||||
SavedStateViewModelFactory(this) { savedStateHandle ->
|
||||
val apiService = ApiConfig.getApiService(sessionManager)
|
||||
val orderRepository = OrderRepository(apiService)
|
||||
AddAddressViewModel(orderRepository, savedStateHandle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityAddAddressBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
sessionManager = SessionManager(this)
|
||||
apiService = ApiConfig.getApiService(sessionManager)
|
||||
locationManager = getSystemService(LOCATION_SERVICE) as LocationManager
|
||||
|
||||
setupToolbar()
|
||||
setupAutoComplete()
|
||||
setupButtonListeners()
|
||||
collectFlows()
|
||||
requestLocationPermission()
|
||||
|
||||
|
||||
}
|
||||
|
||||
private fun viewModelAddAddress(request: CreateAddressRequest) {
|
||||
// Call the private fun in your ViewModel using reflection or expose it in ViewModel
|
||||
val method = AddAddressViewModel::class.java.getDeclaredMethod("addAddress", CreateAddressRequest::class.java)
|
||||
method.isAccessible = true
|
||||
method.invoke(viewModel, request)
|
||||
}
|
||||
// UI setup methods
|
||||
private fun setupToolbar() {
|
||||
binding.toolbar.setNavigationOnClickListener { finish() }
|
||||
}
|
||||
|
||||
private fun setupAutoComplete() {
|
||||
// Set adapters
|
||||
binding.autoCompleteProvinsi.setAdapter(provinceAdapter)
|
||||
binding.autoCompleteKabupaten.setAdapter(cityAdapter)
|
||||
|
||||
// Set listeners
|
||||
binding.autoCompleteProvinsi.setOnItemClickListener { _, _, position, _ ->
|
||||
provinceAdapter.getProvinceId(position)?.let { provinceId ->
|
||||
viewModel.getCities(provinceId)
|
||||
binding.autoCompleteKabupaten.text.clear()
|
||||
}
|
||||
}
|
||||
|
||||
binding.autoCompleteKabupaten.setOnItemClickListener { _, _, position, _ ->
|
||||
cityAdapter.getCityId(position)?.let { cityId ->
|
||||
viewModel.selectedCityId = cityId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupButtonListeners() {
|
||||
binding.buttonSimpan.setOnClickListener {
|
||||
validateAndSubmitForm()
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectFlows() {
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
launch {
|
||||
viewModel.provincesState.collect { state ->
|
||||
handleProvinceState(state)
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
viewModel.citiesState.collect { state ->
|
||||
handleCityState(state)
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
viewModel.addressSubmissionState.collect { state ->
|
||||
handleAddressSubmissionState(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleProvinceState(state: ViewState<List<ProvincesItem>>) {
|
||||
when (state) {
|
||||
is ViewState.Loading -> null //showProvinceLoading(true)
|
||||
is ViewState.Success -> {
|
||||
provinceAdapter.updateData(state.data)
|
||||
}
|
||||
is ViewState.Error -> {
|
||||
showError(state.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCityState(state: ViewState<List<CitiesItem>>) {
|
||||
when (state) {
|
||||
is ViewState.Loading -> null //showCityLoading(true)
|
||||
is ViewState.Success -> {
|
||||
// showCityLoading(false)
|
||||
cityAdapter.updateData(state.data)
|
||||
}
|
||||
is ViewState.Error -> {
|
||||
// showCityLoading(false)
|
||||
showError(state.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleAddressSubmissionState(state: ViewState<String>) {
|
||||
when (state) {
|
||||
is ViewState.Loading -> showSubmitLoading(true)
|
||||
is ViewState.Success -> {
|
||||
showSubmitLoading(false)
|
||||
showSuccessAndFinish(state.data)
|
||||
}
|
||||
is ViewState.Error -> {
|
||||
showSubmitLoading(false)
|
||||
showError(state.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// private fun showProvinceLoading(isLoading: Boolean) {
|
||||
// // Implement province loading indicator
|
||||
// binding.provinceProgressBar?.visibility = if (isLoading) View.VISIBLE else View.GONE
|
||||
// }
|
||||
//
|
||||
// private fun showCityLoading(isLoading: Boolean) {
|
||||
// // Implement city loading indicator
|
||||
// binding.cityProgressBar?.visibility = if (isLoading) View.VISIBLE else View.GONE
|
||||
// }
|
||||
//
|
||||
private fun showSubmitLoading(isLoading: Boolean) {
|
||||
binding.buttonSimpan.isEnabled = !isLoading
|
||||
binding.buttonSimpan.text = if (isLoading) "Menyimpan..." else "Simpan"
|
||||
// You might want to show a progress bar as well
|
||||
}
|
||||
|
||||
private fun showError(message: String) {
|
||||
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
private fun showSuccessAndFinish(message: String) {
|
||||
Toast.makeText(this, "Sukses: $message", Toast.LENGTH_SHORT).show()
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun validateAndSubmitForm() {
|
||||
val lat = latitude
|
||||
val long = longitude
|
||||
|
||||
if (lat == null || long == null) {
|
||||
showError("Lokasi belum terdeteksi")
|
||||
return
|
||||
}
|
||||
|
||||
val street = binding.etDetailAlamat.text.toString()
|
||||
val subDistrict = binding.etKecamatan.text.toString()
|
||||
val postalCode = binding.etKodePos.text.toString()
|
||||
val recipient = binding.etNamaPenerima.text.toString()
|
||||
val phone = binding.etNomorHp.text.toString()
|
||||
val userId = profileUser.userId
|
||||
val isStoreLocation = false
|
||||
|
||||
val provinceId = viewModel.selectedProvinceId
|
||||
val cityId = viewModel.selectedCityId
|
||||
|
||||
if (street.isBlank() || recipient.isBlank() || phone.isBlank()) {
|
||||
showError("Lengkapi semua field wajib")
|
||||
return
|
||||
}
|
||||
|
||||
if (provinceId == null) {
|
||||
showError("Pilih provinsi terlebih dahulu")
|
||||
return
|
||||
}
|
||||
|
||||
if (cityId == null) {
|
||||
showError("Pilih kota/kabupaten terlebih dahulu")
|
||||
return
|
||||
}
|
||||
|
||||
val request = CreateAddressRequest(
|
||||
lat = lat,
|
||||
long = long,
|
||||
street = street,
|
||||
subDistrict = subDistrict,
|
||||
cityId = cityId,
|
||||
provId = provinceId,
|
||||
postCode = postalCode,
|
||||
detailAddress = street,
|
||||
userId = userId,
|
||||
recipient = recipient,
|
||||
phone = phone,
|
||||
isStoreLocation = isStoreLocation
|
||||
)
|
||||
|
||||
viewModel.addAddress(request)
|
||||
}
|
||||
|
||||
private val locationPermissionLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
|
||||
if (granted) getLocation() else Toast.makeText(this, "Izin lokasi ditolak",Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
private fun requestLocationPermission() {
|
||||
locationPermissionLauncher.launch(android.Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
private fun requestLocation() {
|
||||
val isGpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
|
||||
val isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
|
||||
|
||||
if (!isGpsEnabled && !isNetworkEnabled) {
|
||||
Toast.makeText(this, "Provider lokasi tidak tersedia", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
val provider = if (isGpsEnabled) LocationManager.GPS_PROVIDER else LocationManager.NETWORK_PROVIDER
|
||||
|
||||
locationManager.requestSingleUpdate(provider, object : LocationListener {
|
||||
override fun onLocationChanged(location: Location) {
|
||||
latitude = location.latitude
|
||||
longitude = location.longitude
|
||||
}
|
||||
|
||||
override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {}
|
||||
override fun onProviderEnabled(provider: String) {}
|
||||
override fun onProviderDisabled(provider: String) {
|
||||
Toast.makeText(this@AddAddressActivity, "Provider dimatikan", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}, null)
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
if (requestCode == 100 && grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
requestLocation()
|
||||
} else {
|
||||
Toast.makeText(this, "Location permission denied", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,105 @@
|
||||
package com.alya.ecommerce_serang.ui.order
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.alya.ecommerce_serang.data.api.dto.CreateAddressRequest
|
||||
import com.alya.ecommerce_serang.data.api.response.order.CitiesItem
|
||||
import com.alya.ecommerce_serang.data.api.response.order.ProvincesItem
|
||||
import com.alya.ecommerce_serang.data.repository.OrderRepository
|
||||
import com.alya.ecommerce_serang.data.repository.Result
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class AddAddressViewModel(private val repository: OrderRepository, private val savedStateHandle: SavedStateHandle): ViewModel() {
|
||||
// Flow states for data
|
||||
private val _addressSubmissionState = MutableStateFlow<com.alya.ecommerce_serang.ui.order.ViewState<String>>(com.alya.ecommerce_serang.ui.order.ViewState.Loading)
|
||||
val addressSubmissionState = _addressSubmissionState.asStateFlow()
|
||||
|
||||
private val _provincesState = MutableStateFlow<com.alya.ecommerce_serang.ui.order.ViewState<List<ProvincesItem>>>(com.alya.ecommerce_serang.ui.order.ViewState.Loading)
|
||||
val provincesState = _provincesState.asStateFlow()
|
||||
|
||||
private val _citiesState = MutableStateFlow<com.alya.ecommerce_serang.ui.order.ViewState<List<CitiesItem>>>(com.alya.ecommerce_serang.ui.order.ViewState.Loading)
|
||||
val citiesState = _citiesState.asStateFlow()
|
||||
|
||||
// Stored in SavedStateHandle for configuration changes
|
||||
var selectedProvinceId: Int?
|
||||
get() = savedStateHandle.get<Int>("selectedProvinceId")
|
||||
set(value) { savedStateHandle["selectedProvinceId"] = value }
|
||||
|
||||
var selectedCityId: Int?
|
||||
get() = savedStateHandle.get<Int>("selectedCityId")
|
||||
set(value) { savedStateHandle["selectedCityId"] = value }
|
||||
|
||||
init {
|
||||
// Load provinces on initialization
|
||||
getProvinces()
|
||||
}
|
||||
|
||||
fun addAddress(request: CreateAddressRequest){
|
||||
viewModelScope.launch {
|
||||
when (val result = repository.addAddress(request)) {
|
||||
is Result.Success -> {
|
||||
val message = result.data.message // Ambil `message` dari CreateAddressResponse
|
||||
_addressSubmissionState.value = ViewState.Success(message)
|
||||
}
|
||||
is Result.Error -> {
|
||||
_addressSubmissionState.value = ViewState.Error(result.exception.message ?: "Unknown error")
|
||||
}
|
||||
is Result.Loading -> {
|
||||
// Optional, karena sudah set Loading di awal
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getProvinces(){
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val result = repository.getListProvinces()
|
||||
result?.let {
|
||||
_provincesState.value = com.alya.ecommerce_serang.ui.order.ViewState.Success(it.provinces)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("AddAddressViewModel", "Error fetching provinces: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getCities(provinceId: Int){
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
selectedProvinceId = provinceId
|
||||
val result = repository.getListCities(provinceId)
|
||||
result?.let {
|
||||
_citiesState.value = com.alya.ecommerce_serang.ui.order.ViewState.Success(it.cities)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("AddAddressViewModel", "Error fetching cities: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setSelectedProvinceId(id: Int) {
|
||||
selectedProvinceId = id
|
||||
}
|
||||
|
||||
fun setSelectedCityId(id: Int) {
|
||||
selectedCityId = id
|
||||
}
|
||||
|
||||
fun getSelectedProvinceId(): Int? = selectedProvinceId
|
||||
fun getSelectedCityId(): Int? = selectedCityId
|
||||
|
||||
companion object {
|
||||
private const val TAG = "AddAddressViewModel"
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ViewState<out T> {
|
||||
object Loading : com.alya.ecommerce_serang.ui.order.ViewState<Nothing>()
|
||||
data class Success<T>(val data: T) : com.alya.ecommerce_serang.ui.order.ViewState<T>()
|
||||
data class Error(val message: String) : com.alya.ecommerce_serang.ui.order.ViewState<Nothing>()
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package com.alya.ecommerce_serang.ui.order
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.ArrayAdapter
|
||||
import com.alya.ecommerce_serang.data.api.response.order.CitiesItem
|
||||
import com.alya.ecommerce_serang.data.api.response.order.ProvincesItem
|
||||
|
||||
// UI adapters and helpers
|
||||
class ProvinceAdapter(
|
||||
context: Context,
|
||||
resource: Int = android.R.layout.simple_dropdown_item_1line
|
||||
) : ArrayAdapter<String>(context, resource, ArrayList()) {
|
||||
|
||||
private val provinces = ArrayList<ProvincesItem>()
|
||||
|
||||
fun updateData(newProvinces: List<ProvincesItem>) {
|
||||
provinces.clear()
|
||||
provinces.addAll(newProvinces)
|
||||
|
||||
clear()
|
||||
addAll(provinces.map { it.province })
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun getProvinceId(position: Int): Int? {
|
||||
return provinces.getOrNull(position)?.provinceId?.toIntOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
class CityAdapter(
|
||||
context: Context,
|
||||
resource: Int = android.R.layout.simple_dropdown_item_1line
|
||||
) : ArrayAdapter<String>(context, resource, ArrayList()) {
|
||||
|
||||
private val cities = ArrayList<CitiesItem>()
|
||||
|
||||
fun updateData(newCities: List<CitiesItem>) {
|
||||
cities.clear()
|
||||
cities.addAll(newCities)
|
||||
|
||||
clear()
|
||||
addAll(cities.map { it.cityName })
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun getCityId(position: Int): Int? {
|
||||
return cities.getOrNull(position)?.cityId?.toIntOrNull()
|
||||
}
|
||||
}
|
@ -1,7 +1,10 @@
|
||||
package com.alya.ecommerce_serang.utils
|
||||
|
||||
import androidx.lifecycle.AbstractSavedStateViewModelFactory
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.savedstate.SavedStateRegistryOwner
|
||||
|
||||
class BaseViewModelFactory<VM : ViewModel>(
|
||||
private val creator: () -> VM
|
||||
@ -10,4 +13,20 @@ class BaseViewModelFactory<VM : ViewModel>(
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return creator() as T
|
||||
}
|
||||
}
|
||||
|
||||
// Add a new factory for SavedStateHandle ViewModels
|
||||
class SavedStateViewModelFactory<VM : ViewModel>(
|
||||
private val owner: SavedStateRegistryOwner,
|
||||
private val creator: (SavedStateHandle) -> VM
|
||||
) : AbstractSavedStateViewModelFactory(owner, null) {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(
|
||||
key: String,
|
||||
modelClass: Class<T>,
|
||||
handle: SavedStateHandle
|
||||
): T {
|
||||
return creator(handle) as T
|
||||
}
|
||||
}
|
@ -131,7 +131,6 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:hint="Pilih Kabupaten / Kota"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu">
|
||||
|
||||
<AutoCompleteTextView
|
||||
@ -139,6 +138,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="none"
|
||||
android:hint="Masukkan Kabupaten"
|
||||
android:padding="12dp"
|
||||
android:textSize="14sp" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
@ -29,9 +29,11 @@
|
||||
android:textAlignment="textEnd"
|
||||
android:layout_gravity="center"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingVertical="16dp"
|
||||
android:textColor="@color/blue_500"
|
||||
android:fontFamily="@font/dmsans_semibold"
|
||||
android:textSize="14sp"
|
||||
android:clickable="true"
|
||||
android:text="Tambah Alamat"
|
||||
tools:ignore="RtlCompat" />
|
||||
</LinearLayout>
|
||||
|
@ -47,13 +47,23 @@
|
||||
android:layout_gravity="start"
|
||||
android:padding="8dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_location_checkout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:tint="@color/blue_500"
|
||||
android:layout_marginTop="16dp"
|
||||
android:src="@drawable/baseline_location_pin_24"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linear_address"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_location_checkout"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/tv_change_address">
|
||||
|
Reference in New Issue
Block a user