mirror of
https://github.com/shaulascr/ecommerce_serang.git
synced 2025-08-10 09:22:21 +00:00
call detail product and profile
This commit is contained in:
@ -5,7 +5,7 @@ import com.google.gson.annotations.SerializedName
|
||||
data class UserProfile(
|
||||
|
||||
@field:SerializedName("image")
|
||||
val image: Any?,
|
||||
val image: String? = null,
|
||||
|
||||
@field:SerializedName("role")
|
||||
val role: String,
|
||||
|
@ -0,0 +1,33 @@
|
||||
package com.alya.ecommerce_serang.data.api.response
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class AllStoreResponse(
|
||||
|
||||
@field:SerializedName("store")
|
||||
val store: AllStore,
|
||||
|
||||
@field:SerializedName("message")
|
||||
val message: String
|
||||
)
|
||||
|
||||
data class AllStore(
|
||||
|
||||
@field:SerializedName("store_name")
|
||||
val storeName: String,
|
||||
|
||||
@field:SerializedName("description")
|
||||
val description: String,
|
||||
|
||||
@field:SerializedName("store_type")
|
||||
val storeType: String,
|
||||
|
||||
@field:SerializedName("store_location")
|
||||
val storeLocation: String,
|
||||
|
||||
@field:SerializedName("store_image")
|
||||
val storeImage: Any,
|
||||
|
||||
@field:SerializedName("status")
|
||||
val status: String
|
||||
)
|
@ -0,0 +1,33 @@
|
||||
package com.alya.ecommerce_serang.data.api.response
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class DetailStoreProductResponse(
|
||||
|
||||
@field:SerializedName("store")
|
||||
val store: StoreProduct,
|
||||
|
||||
@field:SerializedName("message")
|
||||
val message: String
|
||||
)
|
||||
|
||||
data class StoreProduct(
|
||||
|
||||
@field:SerializedName("store_name")
|
||||
val storeName: String,
|
||||
|
||||
@field:SerializedName("description")
|
||||
val description: String,
|
||||
|
||||
@field:SerializedName("store_type")
|
||||
val storeType: String,
|
||||
|
||||
@field:SerializedName("store_location")
|
||||
val storeLocation: String,
|
||||
|
||||
@field:SerializedName("store_image")
|
||||
val storeImage: Any,
|
||||
|
||||
@field:SerializedName("status")
|
||||
val status: String
|
||||
)
|
@ -17,7 +17,7 @@ data class Product(
|
||||
val storeId: Int,
|
||||
|
||||
@field:SerializedName("image")
|
||||
val image: String,
|
||||
val image: String? = null,
|
||||
|
||||
@field:SerializedName("rating")
|
||||
val rating: String,
|
||||
|
@ -8,6 +8,7 @@ import com.alya.ecommerce_serang.data.api.response.CategoryResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.LoginResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.OtpResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.ProductResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.ProfileResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.RegisterResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.ReviewProductResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.StoreResponse
|
||||
@ -51,6 +52,10 @@ interface ApiService {
|
||||
@Path("id") productId: Int
|
||||
): Response<ProductResponse>
|
||||
|
||||
@GET("profile")
|
||||
suspend fun getUserProfile(): Response<ProfileResponse>
|
||||
|
||||
|
||||
@GET("mystore")
|
||||
fun getStore (): Call<StoreResponse>
|
||||
}
|
@ -37,6 +37,7 @@ class ProductRepository(private val apiService: ApiService) {
|
||||
if (response.isSuccessful) {
|
||||
response.body()
|
||||
} else {
|
||||
Log.e("ProductRepository", "Error: ${response.errorBody()?.string()}")
|
||||
null
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
@ -70,6 +71,7 @@ class ProductRepository(private val apiService: ApiService) {
|
||||
if (response.isSuccessful) {
|
||||
response.body()?.reviews // Ambil daftar review dari response
|
||||
} else {
|
||||
Log.e("ProductRepository", "Error: ${response.errorBody()?.string()}")
|
||||
null
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
@ -3,6 +3,7 @@ package com.alya.ecommerce_serang.data.repository
|
||||
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.UserProfile
|
||||
import com.alya.ecommerce_serang.data.api.response.LoginResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.OtpResponse
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiService
|
||||
@ -42,6 +43,23 @@ class UserRepository(private val apiService: ApiService) {
|
||||
Result.Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun fetchUserProfile(): Result<UserProfile?> {
|
||||
return try {
|
||||
val response = apiService.getUserProfile()
|
||||
if (response.isSuccessful) {
|
||||
response.body()?.user?.let {
|
||||
Result.Success(it) // ✅ Returning only UserProfile
|
||||
} ?: Result.Error(Exception("User data not found"))
|
||||
} else {
|
||||
Result.Error(Exception("Error fetching profile: ${response.code()}"))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Result.Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
sealed class Result<out T> {
|
||||
|
@ -107,7 +107,7 @@ class HomeFragment : Fragment() {
|
||||
binding.loading.root.isVisible = false
|
||||
binding.error.root.isVisible = false
|
||||
binding.home.isVisible = true
|
||||
productAdapter?.updateLimitedProducts(state.products)
|
||||
productAdapter?.updateLimitedProducts(state.products) // Ensure productAdapter is initialized
|
||||
}
|
||||
is HomeUiState.Error -> {
|
||||
binding.loading.root.isVisible = false
|
||||
@ -123,7 +123,7 @@ class HomeFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
viewModel.categories.collect { categories ->
|
||||
Log.d("Categories", "Updated Categories: $categories")
|
||||
|
@ -4,13 +4,16 @@ import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.alya.ecommerce_serang.BuildConfig.BASE_URL
|
||||
import com.alya.ecommerce_serang.R
|
||||
import com.alya.ecommerce_serang.data.api.response.Product
|
||||
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.ProductRepository
|
||||
import com.alya.ecommerce_serang.databinding.ActivityDetailProductBinding
|
||||
import com.alya.ecommerce_serang.ui.home.HomeViewModel
|
||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||
import com.alya.ecommerce_serang.utils.SessionManager
|
||||
import com.bumptech.glide.Glide
|
||||
|
||||
class DetailProductActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityDetailProductBinding
|
||||
@ -21,7 +24,7 @@ class DetailProductActivity : AppCompatActivity() {
|
||||
BaseViewModelFactory {
|
||||
val apiService = ApiConfig.getApiService(sessionManager)
|
||||
val productRepository = ProductRepository(apiService)
|
||||
HomeViewModel(productRepository)
|
||||
ProductViewModel(productRepository)
|
||||
}
|
||||
}
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@ -51,21 +54,42 @@ class DetailProductActivity : AppCompatActivity() {
|
||||
if (product != null) {
|
||||
Log.d("ProductDetail", "Name: ${product.productName}, Price: ${product.price}")
|
||||
// Update UI here, e.g., show in a TextView or ImageView
|
||||
binding.tvProductName.text = product.productName
|
||||
binding.tvPrice.text = product.price
|
||||
binding.tvSold.text = product.totalSold.toString()
|
||||
binding.tvRating.text = product.rating
|
||||
binding.tvWeight.text = product.weight.toString()
|
||||
binding.tvStock.text = product.stock.toString()
|
||||
binding.tvCategory.text = product.productCategory
|
||||
binding.tvDescription.text = product.description
|
||||
binding.tvSellerName.text = product.storeId.toString()
|
||||
|
||||
viewModel.loadProductDetail(productId)
|
||||
|
||||
} else {
|
||||
Log.e("ProductDetail", "Failed to fetch product details")
|
||||
}
|
||||
}
|
||||
observeProductDetail()
|
||||
}
|
||||
private fun observeProductDetail() {
|
||||
viewModel.productDetail.observe(this) { product ->
|
||||
product?.let { updateUI(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateUI(product: Product){
|
||||
binding.tvProductName.text = product.productName
|
||||
binding.tvPrice.text = product.price
|
||||
binding.tvSold.text = product.totalSold.toString()
|
||||
binding.tvRating.text = product.rating
|
||||
binding.tvWeight.text = product.weight.toString()
|
||||
binding.tvStock.text = product.stock.toString()
|
||||
binding.tvCategory.text = product.productCategory
|
||||
binding.tvDescription.text = product.description
|
||||
binding.tvSellerName.text = product.storeId.toString()
|
||||
|
||||
val fullImageUrl = when (val img = product.image) {
|
||||
is String -> {
|
||||
if (img.startsWith("/")) BASE_URL + img.substring(1) else img
|
||||
}
|
||||
else -> R.drawable.placeholder_image // Default image for null
|
||||
}
|
||||
Log.d("ProductAdapter", "Loading image: $fullImageUrl")
|
||||
|
||||
Glide.with(this)
|
||||
.load(fullImageUrl)
|
||||
.placeholder(R.drawable.placeholder_image)
|
||||
.into(binding.ivProductImage)
|
||||
}
|
||||
}
|
@ -1,21 +1,85 @@
|
||||
package com.alya.ecommerce_serang.ui.profile
|
||||
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.alya.ecommerce_serang.R
|
||||
import com.alya.ecommerce_serang.data.api.dto.UserProfile
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiService
|
||||
import com.alya.ecommerce_serang.data.repository.UserRepository
|
||||
import com.alya.ecommerce_serang.databinding.ActivityDetailProfileBinding
|
||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||
import com.alya.ecommerce_serang.utils.SessionManager
|
||||
import com.bumptech.glide.Glide
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import java.util.TimeZone
|
||||
|
||||
class DetailProfileActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityDetailProfileBinding
|
||||
private lateinit var apiService: ApiService
|
||||
private lateinit var sessionManager: SessionManager
|
||||
|
||||
private val viewModel: ProfileViewModel by viewModels {
|
||||
BaseViewModelFactory {
|
||||
val apiService = ApiConfig.getApiService(sessionManager)
|
||||
val userRepository = UserRepository(apiService)
|
||||
ProfileViewModel(userRepository)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityDetailProfileBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
sessionManager = SessionManager(this)
|
||||
apiService = ApiConfig.getApiService(sessionManager)
|
||||
|
||||
enableEdgeToEdge()
|
||||
setContentView(R.layout.activity_detail_profile)
|
||||
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
|
||||
// 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
|
||||
// }
|
||||
|
||||
viewModel.loadUserProfile()
|
||||
|
||||
viewModel.userProfile.observe(this){ user ->
|
||||
user?.let { updateProfile(it) }
|
||||
}
|
||||
|
||||
viewModel.errorMessage.observe(this) { error ->
|
||||
Toast.makeText(this, error, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateProfile(user: UserProfile){
|
||||
|
||||
binding.tvNameUser.setText(user.name.toString())
|
||||
binding.tvUsername.setText(user.username)
|
||||
binding.tvEmailUser.setText(user.email)
|
||||
binding.tvDateBirth.setText(formatDate(user.birthDate))
|
||||
binding.tvNumberPhoneUser.setText(user.phone)
|
||||
|
||||
if (user.image != null && user.image is String) {
|
||||
Glide.with(this)
|
||||
.load(user.image)
|
||||
.into(binding.profileImage)
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatDate(dateString: String): String {
|
||||
return try {
|
||||
val inputFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault()) //from json
|
||||
inputFormat.timeZone = TimeZone.getTimeZone("UTC") //get timezone
|
||||
val outputFormat = SimpleDateFormat("dd MMMM yyyy", Locale.getDefault()) // new format
|
||||
val date = inputFormat.parse(dateString) // Parse from json format
|
||||
outputFormat.format(date!!) // convert to new format
|
||||
} catch (e: Exception) {
|
||||
dateString // Return original if error occurs
|
||||
}
|
||||
}
|
||||
}
|
@ -1,22 +1,43 @@
|
||||
package com.alya.ecommerce_serang.ui.profile
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
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.BuildConfig.BASE_URL
|
||||
import com.alya.ecommerce_serang.R
|
||||
import com.alya.ecommerce_serang.data.api.dto.UserProfile
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||
import com.alya.ecommerce_serang.data.repository.UserRepository
|
||||
import com.alya.ecommerce_serang.databinding.FragmentProfileBinding
|
||||
import com.alya.ecommerce_serang.ui.profile.mystore.TokoSayaActivity
|
||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||
import com.alya.ecommerce_serang.utils.SessionManager
|
||||
import com.bumptech.glide.Glide
|
||||
|
||||
class ProfileFragment : Fragment() {
|
||||
|
||||
private var _binding: FragmentProfileBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
private lateinit var viewModel: ProfileViewModel
|
||||
private lateinit var sessionManager: SessionManager
|
||||
|
||||
private val viewModel: ProfileViewModel by viewModels {
|
||||
BaseViewModelFactory {
|
||||
val apiService = ApiConfig.getApiService(sessionManager)
|
||||
val userRepository = UserRepository(apiService)
|
||||
ProfileViewModel(userRepository)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
sessionManager = SessionManager(requireContext())
|
||||
|
||||
// TODO: Use the ViewModel
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
@ -26,4 +47,49 @@ class ProfileFragment : Fragment() {
|
||||
_binding = FragmentProfileBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
observeUserProfile()
|
||||
viewModel.loadUserProfile()
|
||||
|
||||
binding.cardBukaToko.setOnClickListener{
|
||||
val intentBuka = Intent(requireContext(), TokoSayaActivity::class.java)
|
||||
startActivity(intentBuka)
|
||||
}
|
||||
|
||||
binding.btnDetailProfile.setOnClickListener{
|
||||
val intentDetail = Intent(requireContext(), DetailProfileActivity::class.java)
|
||||
startActivity(intentDetail)
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeUserProfile() {
|
||||
viewModel.userProfile.observe(viewLifecycleOwner) { user ->
|
||||
user?.let { updateUI(it) }
|
||||
}
|
||||
viewModel.errorMessage.observe(viewLifecycleOwner) { errorMessage ->
|
||||
Toast.makeText(requireContext(), errorMessage, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateUI(user: UserProfile) = with(binding){
|
||||
val fullImageUrl = when (val img = user.image) {
|
||||
is String -> {
|
||||
if (img.startsWith("/")) BASE_URL + img.substring(1) else img
|
||||
}
|
||||
else -> R.drawable.placeholder_image // Default image for null
|
||||
}
|
||||
|
||||
Log.d("ProductAdapter", "Loading image: $fullImageUrl")
|
||||
|
||||
tvName.text = user.name.toString()
|
||||
tvUsername.text = user.username.toString()
|
||||
|
||||
// Load image using Glide
|
||||
Glide.with(requireContext())
|
||||
.load(fullImageUrl)
|
||||
.placeholder(R.drawable.placeholder_image)
|
||||
.into(profileImage)
|
||||
}
|
||||
}
|
@ -1,7 +1,28 @@
|
||||
package com.alya.ecommerce_serang.ui.profile
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.alya.ecommerce_serang.data.api.dto.UserProfile
|
||||
import com.alya.ecommerce_serang.data.repository.Result
|
||||
import com.alya.ecommerce_serang.data.repository.UserRepository
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ProfileViewModel : ViewModel() {
|
||||
// TODO: Implement the ViewModel
|
||||
class ProfileViewModel(private val userRepository: UserRepository) : ViewModel() {
|
||||
private val _userProfile = MutableLiveData<UserProfile?>()
|
||||
val userProfile: LiveData<UserProfile?> = _userProfile
|
||||
|
||||
private val _errorMessage = MutableLiveData<String>()
|
||||
val errorMessage : LiveData<String> = _errorMessage
|
||||
|
||||
fun loadUserProfile(){
|
||||
viewModelScope.launch {
|
||||
when (val result = userRepository.fetchUserProfile()){
|
||||
is Result.Success -> _userProfile.postValue(result.data)
|
||||
is Result.Error -> _errorMessage.postValue(result.exception.message ?: "Unknown Error")
|
||||
is Result.Loading -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -104,6 +104,7 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/card_profile">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/tv_name_user"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Nama"
|
||||
@ -120,6 +121,7 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/til_nama">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/tv_username"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Username"
|
||||
@ -136,6 +138,7 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/til_username">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/tv_email_user"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Email"
|
||||
@ -152,6 +155,7 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/til_email">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/tv_number_phone_user"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Nomor Handphone"
|
||||
@ -168,6 +172,7 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/til_nomor_handphone">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/tv_date_birth"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Tanggal Lahir"
|
||||
|
@ -45,6 +45,8 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="Detail Profil"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/profileImage" />
|
||||
|
||||
@ -55,6 +57,8 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
app:cardElevation="4dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/profileImage">
|
||||
|
||||
|
Reference in New Issue
Block a user