diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index dd10380..50af9ab 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -58,7 +58,7 @@ android:exported="false" /> + android:exported="false"> diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/UserProfile.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/UserProfile.kt new file mode 100644 index 0000000..333a2ac --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/UserProfile.kt @@ -0,0 +1,30 @@ +package com.alya.ecommerce_serang.data.api.dto + +import com.google.gson.annotations.SerializedName + +data class UserProfile( + + @field:SerializedName("image") + val image: Any?, + + @field:SerializedName("role") + val role: String, + + @field:SerializedName("user_id") + val userId: Int, + + @field:SerializedName("phone") + val phone: String, + + @field:SerializedName("birth_date") + val birthDate: String, + + @field:SerializedName("name") + val name: String, + + @field:SerializedName("email") + val email: String, + + @field:SerializedName("username") + val username: String +) \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/ProductResponse.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/ProductResponse.kt index 26b1130..041a2a4 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/ProductResponse.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/ProductResponse.kt @@ -35,7 +35,7 @@ data class Product( val isPreOrder: Boolean, @field:SerializedName("duration") - val duration: Any, + val duration: Any?, @field:SerializedName("category_id") val categoryId: Int, diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/ProfileResponse.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/ProfileResponse.kt new file mode 100644 index 0000000..d9fe08d --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/ProfileResponse.kt @@ -0,0 +1,15 @@ +package com.alya.ecommerce_serang.data.api.response + +import com.alya.ecommerce_serang.data.api.dto.UserProfile +import com.google.gson.annotations.SerializedName + +data class ProfileResponse( + + @field:SerializedName("message") + val message: String, + + @field:SerializedName("user") + val user: UserProfile +) + + diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/ReviewProductResponse.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/ReviewProductResponse.kt new file mode 100644 index 0000000..d7313b5 --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/ReviewProductResponse.kt @@ -0,0 +1,39 @@ +package com.alya.ecommerce_serang.data.api.response + +import com.google.gson.annotations.SerializedName + +data class ReviewProductResponse( + + @field:SerializedName("reviews") + val reviews: List, + + @field:SerializedName("message") + val message: String +) + +data class ReviewsItem( + + @field:SerializedName("order_item_id") + val orderItemId: Int, + + @field:SerializedName("review_date") + val reviewDate: String, + + @field:SerializedName("user_image") + val userImage: Any, + + @field:SerializedName("product_id") + val productId: Int, + + @field:SerializedName("rating") + val rating: Int, + + @field:SerializedName("review_text") + val reviewText: String, + + @field:SerializedName("product_name") + val productName: String, + + @field:SerializedName("username") + val username: String +) 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 0d4a120..5a2954a 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 @@ -9,6 +9,7 @@ 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.RegisterResponse +import com.alya.ecommerce_serang.data.api.response.ReviewProductResponse import com.alya.ecommerce_serang.data.api.response.StoreResponse import retrofit2.Call import retrofit2.Response @@ -40,10 +41,15 @@ interface ApiService { @GET("product") suspend fun getAllProduct(): Response - @GET("product/detail/{id}") - fun getDetailProduct ( + @GET("product/review/{id}") + suspend fun getProductReview( @Path("id") productId: Int - ): Call + ): Response + + @GET("product/detail/{id}") + suspend fun getDetailProduct ( + @Path("id") productId: Int + ): Response @GET("mystore") fun getStore (): Call diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/model/Product.kt b/app/src/main/java/com/alya/ecommerce_serang/data/model/Product.kt deleted file mode 100644 index a5f79ea..0000000 --- a/app/src/main/java/com/alya/ecommerce_serang/data/model/Product.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.alya.ecommerce_serang.data.model - -import com.google.gson.annotations.SerializedName - -data class Product( - - @field:SerializedName("store_id") - val storeId: Int, - - @field:SerializedName("image") - val image: String, - - @field:SerializedName("rating") - val rating: String, - - @field:SerializedName("description") - val description: String, - - @field:SerializedName("weight") - val weight: Int, - - @field:SerializedName("product_name") - val productName: String, - - @field:SerializedName("is_pre_order") - val isPreOrder: Boolean, - - @field:SerializedName("duration") - val duration: Any, - - @field:SerializedName("category_id") - val categoryId: Int, - - @field:SerializedName("price") - val price: String, - - @field:SerializedName("product_id") - val productId: Int, - - @field:SerializedName("min_order") - val minOrder: Int, - - @field:SerializedName("total_sold") - val totalSold: Int, - - @field:SerializedName("stock") - val stock: Int, - - @field:SerializedName("product_category") - val productCategory: String -) \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/repository/ProductRepository.kt b/app/src/main/java/com/alya/ecommerce_serang/data/repository/ProductRepository.kt index cfe5e8a..dcf5402 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/data/repository/ProductRepository.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/data/repository/ProductRepository.kt @@ -3,6 +3,8 @@ package com.alya.ecommerce_serang.data.repository import android.util.Log import com.alya.ecommerce_serang.data.api.dto.CategoryItem import com.alya.ecommerce_serang.data.api.dto.ProductsItem +import com.alya.ecommerce_serang.data.api.response.ProductResponse +import com.alya.ecommerce_serang.data.api.response.ReviewsItem import com.alya.ecommerce_serang.data.api.retrofit.ApiService import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -29,6 +31,19 @@ class ProductRepository(private val apiService: ApiService) { } } + suspend fun fetchProductDetail(productId: Int): ProductResponse? { + return try { + val response = apiService.getDetailProduct(productId) + if (response.isSuccessful) { + response.body() + } else { + null + } + } catch (e: Exception) { + null + } + } + suspend fun getAllCategories(): Result> = withContext(Dispatchers.IO) { try { @@ -49,4 +64,17 @@ class ProductRepository(private val apiService: ApiService) { } } + suspend fun fetchProductReview(productId: Int): List? { + return try { + val response = apiService.getProductReview(productId) + if (response.isSuccessful) { + response.body()?.reviews // Ambil daftar review dari response + } else { + null + } + } catch (e: Exception) { + null + } + } + } \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/home/HomeFragment.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/home/HomeFragment.kt index 902ab06..279641c 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/home/HomeFragment.kt @@ -1,5 +1,6 @@ package com.alya.ecommerce_serang.ui.home +import android.content.Intent import android.os.Bundle import android.util.Log import android.view.LayoutInflater @@ -18,6 +19,7 @@ import com.alya.ecommerce_serang.data.api.dto.ProductsItem import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig import com.alya.ecommerce_serang.data.repository.ProductRepository import com.alya.ecommerce_serang.databinding.FragmentHomeBinding +import com.alya.ecommerce_serang.ui.product.DetailProductActivity import com.alya.ecommerce_serang.utils.BaseViewModelFactory import com.alya.ecommerce_serang.utils.HorizontalMarginItemDecoration import com.alya.ecommerce_serang.utils.SessionManager @@ -159,7 +161,9 @@ class HomeFragment : Fragment() { private fun handleProductClick(product: ProductsItem) { - + val intent = Intent(requireContext(), DetailProductActivity::class.java) + intent.putExtra("PRODUCT_ID", product.id) // Pass product ID + startActivity(intent) } private fun handleCategoryProduct(category: CategoryItem) { diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/product/DetailProductActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/product/DetailProductActivity.kt index 284f79d..cc5c7ea 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/product/DetailProductActivity.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/product/DetailProductActivity.kt @@ -1,21 +1,71 @@ package com.alya.ecommerce_serang.ui.product import android.os.Bundle -import androidx.activity.enableEdgeToEdge +import android.util.Log +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.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 class DetailProductActivity : AppCompatActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - enableEdgeToEdge() - setContentView(R.layout.activity_detail_product) - 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: ActivityDetailProductBinding + private lateinit var apiService: ApiService + private lateinit var sessionManager: SessionManager + + private val viewModel: ProductViewModel by viewModels { + BaseViewModelFactory { + val apiService = ApiConfig.getApiService(sessionManager) + val productRepository = ProductRepository(apiService) + HomeViewModel(productRepository) } } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityDetailProductBinding.inflate(layoutInflater) + setContentView(binding.root) + + sessionManager = SessionManager(this) + apiService = ApiConfig.getApiService(sessionManager) + + val productId = intent.getIntExtra("PRODUCT_ID", -1) + if (productId == -1) { + Log.e("DetailProductActivity", "Invalid Product ID") + finish() // Close activity if no valid ID + return + } + +// 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.loadProductDetail(productId) + + viewModel.productDetail.observe(this) { product -> + 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() + + + } else { + Log.e("ProductDetail", "Failed to fetch product details") + } + } + + } } \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/product/ProductViewModel.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/product/ProductViewModel.kt new file mode 100644 index 0000000..f822239 --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/product/ProductViewModel.kt @@ -0,0 +1,22 @@ +package com.alya.ecommerce_serang.ui.product + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.alya.ecommerce_serang.data.api.response.Product +import com.alya.ecommerce_serang.data.repository.ProductRepository +import kotlinx.coroutines.launch + +class ProductViewModel(private val repository: ProductRepository) : ViewModel() { + + private val _productDetail = MutableLiveData() + val productDetail: LiveData get() = _productDetail + + fun loadProductDetail(productId: Int) { + viewModelScope.launch { + val result = repository.fetchProductDetail(productId) + _productDetail.value = result?.product + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/DetailProfileFragment.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/DetailProfileFragment.kt new file mode 100644 index 0000000..c703316 --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/DetailProfileFragment.kt @@ -0,0 +1,60 @@ +package com.alya.ecommerce_serang.ui.profile + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.alya.ecommerce_serang.R + +// TODO: Rename parameter arguments, choose names that match +// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER +private const val ARG_PARAM1 = "param1" +private const val ARG_PARAM2 = "param2" + +/** + * A simple [Fragment] subclass. + * Use the [DetailProfileFragment.newInstance] factory method to + * create an instance of this fragment. + */ +class DetailProfileFragment : Fragment() { + // TODO: Rename and change types of parameters + private var param1: String? = null + private var param2: String? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + arguments?.let { + param1 = it.getString(ARG_PARAM1) + param2 = it.getString(ARG_PARAM2) + } + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_detail_profile, container, false) + } + + companion object { + /** + * Use this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @param param1 Parameter 1. + * @param param2 Parameter 2. + * @return A new instance of fragment DetailProfileFragment. + */ + // TODO: Rename and change types and number of parameters + @JvmStatic + fun newInstance(param1: String, param2: String) = + DetailProfileFragment().apply { + arguments = Bundle().apply { + putString(ARG_PARAM1, param1) + putString(ARG_PARAM2, param2) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/baseline_add_24.xml b/app/src/main/res/drawable/baseline_add_24.xml new file mode 100644 index 0000000..2ae27b8 --- /dev/null +++ b/app/src/main/res/drawable/baseline_add_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/circle_background.xml b/app/src/main/res/drawable/circle_background.xml new file mode 100644 index 0000000..d99ff1b --- /dev/null +++ b/app/src/main/res/drawable/circle_background.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_edit.xml b/app/src/main/res/drawable/ic_edit.xml new file mode 100644 index 0000000..fab852e --- /dev/null +++ b/app/src/main/res/drawable/ic_edit.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/layout/activity_detail_product.xml b/app/src/main/res/layout/activity_detail_product.xml index 433367a..23bd083 100644 --- a/app/src/main/res/layout/activity_detail_product.xml +++ b/app/src/main/res/layout/activity_detail_product.xml @@ -1,208 +1,469 @@ - - - - + + android:layout_height="match_parent" + app:layout_behavior="@string/appbar_scrolling_view_behavior"> + + android:orientation="vertical"> + + + + + + app:cardElevation="0dp"> - - - - - - - + android:padding="16dp"> + tools:text="Rp65.000" /> + tools:text="Keripik Ikan Tenggiri" /> - + android:layout_marginTop="8dp" + android:gravity="center_vertical" + android:orientation="horizontal"> - + - + + + + + + - - - - - + android:layout_height="wrap_content"/> - + + app:cardElevation="0dp"> - + - + - + - + + + + + + - - + - + + app:cardElevation="0dp"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + android:backgroundTint="@color/white" + app:contentInsetStart="0dp"> + android:layout_height="56dp" + android:orientation="horizontal"> -