diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 7b3006b..d124cf2 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -6,14 +6,13 @@ - diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2a25f1f..07c27e4 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,3 +1,5 @@ +import org.gradle.api.tasks.compile.JavaCompile + plugins { alias(libs.plugins.android.application) alias(libs.plugins.jetbrains.kotlin.android) @@ -84,3 +86,4 @@ dependencies { // kapt("androidx.hilt:hilt-compiler:1.0.0") } + diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/ViewStoreProductsResponse.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/ViewStoreProductsResponse.kt new file mode 100644 index 0000000..c933ac7 --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/ViewStoreProductsResponse.kt @@ -0,0 +1,13 @@ +package com.alya.ecommerce_serang.data.api.response + +import com.alya.ecommerce_serang.data.api.dto.ProductsItem +import com.google.gson.annotations.SerializedName + +data class ViewStoreProductsResponse( + + @field:SerializedName("message") + val message: String? = null, + + @field:SerializedName("products") + val products: List? = null +) 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 f616739..1ad98bf 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 @@ -13,6 +13,7 @@ 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 +import com.alya.ecommerce_serang.data.api.response.ViewStoreProductsResponse import retrofit2.Call import retrofit2.Response import retrofit2.http.Body @@ -64,4 +65,8 @@ interface ApiService { @GET("mystore") suspend fun getStore (): Response + + @GET("mystore/product") // Replace with actual endpoint + suspend fun getStoreProduct(): Response + } \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/model/BalanceTransaction.kt b/app/src/main/java/com/alya/ecommerce_serang/data/model/BalanceTransaction.kt deleted file mode 100644 index 227f35d..0000000 --- a/app/src/main/java/com/alya/ecommerce_serang/data/model/BalanceTransaction.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.alya.ecommerce_serang.data.model - -data class BalanceTransaction( - val date: String, - val balanceTransTitle: String, - val balanceTransDesc: String, - val balanceTransAmount: String, -) 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 519b5d4..f65d677 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 @@ -79,6 +79,16 @@ class ProductRepository(private val apiService: ApiService) { null } } + + suspend fun fetchMyStoreProducts(): List { + val response = apiService.getStoreProduct() + if (response.isSuccessful) { + val responseBody = response.body() + return responseBody?.products?.filterNotNull() ?: emptyList() + } else { + throw Exception("Failed to fetch store products: ${response.message()}") + } + } } // suspend fun fetchStoreDetail(storeId: Int): Store? { 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 index e349bfa..c048a13 100644 --- 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 @@ -4,10 +4,12 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.alya.ecommerce_serang.data.api.dto.ProductsItem import com.alya.ecommerce_serang.data.api.dto.Store import com.alya.ecommerce_serang.data.api.response.Product import com.alya.ecommerce_serang.data.api.response.ReviewsItem import com.alya.ecommerce_serang.data.repository.ProductRepository +import com.alya.ecommerce_serang.data.repository.Result import kotlinx.coroutines.launch class ProductViewModel(private val repository: ProductRepository) : ViewModel() { @@ -21,6 +23,10 @@ class ProductViewModel(private val repository: ProductRepository) : ViewModel() private val _reviewProduct = MutableLiveData>() val reviewProduct: LiveData> get() = _reviewProduct + // For List of Products in My Store + private val _productList = MutableLiveData>>() + val productList: LiveData>> get() = _productList + fun loadProductDetail(productId: Int) { viewModelScope.launch { val result = repository.fetchProductDetail(productId) @@ -34,11 +40,24 @@ class ProductViewModel(private val repository: ProductRepository) : ViewModel() _reviewProduct.value = reviews ?: emptyList() } } -} -// fun loadStoreDetail(storeId: Int){ + fun loadMyStoreProducts() { + viewModelScope.launch { + _productList.value = Result.Loading + try { + val result = repository.fetchMyStoreProducts() + _productList.value = Result.Success(result) + } catch (e: Exception) { + _productList.value = Result.Error(e) + } + } + } + + // Optional: for store detail if you need it later +// fun loadStoreDetail(storeId: Int) { // viewModelScope.launch { // val storeResult = repository.fetchStoreDetail(storeId) // _storeDetail.value = storeResult // } -// } \ No newline at end of file +// } +} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/balance/BalanceTransactionAdapter.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/balance/BalanceTransactionAdapter.kt index efa6f87..e106b1d 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/balance/BalanceTransactionAdapter.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/balance/BalanceTransactionAdapter.kt @@ -1,7 +1,5 @@ package com.alya.ecommerce_serang.ui.profile.mystore.balance -import com.alya.ecommerce_serang.data.model.BalanceTransaction - /* class BalanceTransactionAdapter(private val balanceTransactionList: List) : RecyclerView.Adapter() { diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/ProductActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/ProductActivity.kt index 9505316..3b9db9b 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/ProductActivity.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/ProductActivity.kt @@ -1,21 +1,86 @@ package com.alya.ecommerce_serang.ui.profile.mystore.product +import android.content.Intent import android.os.Bundle -import androidx.activity.enableEdgeToEdge +import android.view.View +import android.widget.Toast +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.recyclerview.widget.LinearLayoutManager +import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig +import com.alya.ecommerce_serang.data.repository.ProductRepository +import com.alya.ecommerce_serang.data.repository.Result +import com.alya.ecommerce_serang.databinding.ActivityProductBinding +import com.alya.ecommerce_serang.ui.product.ProductViewModel +import com.alya.ecommerce_serang.utils.BaseViewModelFactory +import com.alya.ecommerce_serang.utils.SessionManager class ProductActivity : AppCompatActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - enableEdgeToEdge() - setContentView(R.layout.activity_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: ActivityProductBinding + private lateinit var sessionManager: SessionManager + + private val viewModel: ProductViewModel by viewModels { + BaseViewModelFactory { + sessionManager = SessionManager(this) + val apiService = ApiConfig.getApiService(sessionManager) + val productRepository = ProductRepository(apiService) + ProductViewModel(productRepository) } } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityProductBinding.inflate(layoutInflater) + setContentView(binding.root) + + setupHeader() + setupRecyclerView() + + observeViewModel() + + binding.progressBar.visibility = View.VISIBLE + viewModel.loadMyStoreProducts() + + } + + private fun observeViewModel() { + viewModel.productList.observe(this) { result -> + when (result) { + is Result.Loading -> { + binding.progressBar.visibility = View.VISIBLE + } + is Result.Success -> { + binding.progressBar.visibility = View.GONE + val products = result.data + binding.rvStoreProduct.adapter = ProductAdapter(products) { + Toast.makeText(this, "Clicked: ${it.name}", Toast.LENGTH_SHORT).show() + } + } + is Result.Error -> { + binding.progressBar.visibility = View.GONE + Toast.makeText(this, "Failed to load products: ${result.exception.message}", Toast.LENGTH_SHORT).show() + } + } + } + } + + private fun setupHeader() { + binding.header.headerTitle.text = "Produk Saya" + binding.header.headerRightText.visibility = View.VISIBLE + + binding.header.headerLeftIcon.setOnClickListener { + onBackPressedDispatcher.onBackPressed() + } + + binding.header.headerRightText.setOnClickListener { + startActivity(Intent(this, AddProductActivity::class.java)) + } + } + + + + private fun setupRecyclerView() { + binding.rvStoreProduct.layoutManager = LinearLayoutManager(this) + } } \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/ProductAdapter.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/ProductAdapter.kt new file mode 100644 index 0000000..09ff20c --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/ProductAdapter.kt @@ -0,0 +1,63 @@ +package com.alya.ecommerce_serang.ui.profile.mystore.product + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.RecyclerView +import com.alya.ecommerce_serang.R +import com.alya.ecommerce_serang.data.api.dto.ProductsItem +import com.bumptech.glide.Glide + +class ProductAdapter( + private val products: List, + private val onItemClick: (ProductsItem) -> Unit +) : RecyclerView.Adapter() { + + inner class ProductViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + private val ivProduct: ImageView = itemView.findViewById(R.id.iv_product) + private val tvProductName: TextView = itemView.findViewById(R.id.tv_product_name) + private val tvProductPrice: TextView = itemView.findViewById(R.id.tv_product_price) + private val tvProductStock: TextView = itemView.findViewById(R.id.tv_product_stock) + private val tvProductStatus: TextView = itemView.findViewById(R.id.tv_product_status) + + fun bind(product: ProductsItem) { + tvProductName.text = product.name + tvProductPrice.text = "Rp${product.price}" + tvProductStock.text = "Stok: ${product.stock}" + tvProductStatus.text = product.status + + // Change color depending on status + tvProductStatus.setTextColor( + ContextCompat.getColor( + itemView.context, + if (product.status.equals("active", true)) + R.color.darkblue_500 else R.color.black_500 + ) + ) + + Glide.with(itemView.context) + .load(product.image) + .placeholder(R.drawable.placeholder_image) + .into(ivProduct) + + itemView.setOnClickListener { + onItemClick(product) + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductViewHolder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.item_store_product, parent, false) + return ProductViewHolder(view) + } + + override fun getItemCount(): Int = products.size + + override fun onBindViewHolder(holder: ProductViewHolder, position: Int) { + holder.bind(products[position]) + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_product_active.xml b/app/src/main/res/drawable/bg_product_active.xml new file mode 100644 index 0000000..a652973 --- /dev/null +++ b/app/src/main/res/drawable/bg_product_active.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_product_inactive.xml b/app/src/main/res/drawable/bg_product_inactive.xml new file mode 100644 index 0000000..923c799 --- /dev/null +++ b/app/src/main/res/drawable/bg_product_inactive.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_search.png b/app/src/main/res/drawable/ic_search.png new file mode 100644 index 0000000..4c0332e Binary files /dev/null and b/app/src/main/res/drawable/ic_search.png differ diff --git a/app/src/main/res/layout/activity_add_product.xml b/app/src/main/res/layout/activity_add_product.xml index ba6e432..6f34b56 100644 --- a/app/src/main/res/layout/activity_add_product.xml +++ b/app/src/main/res/layout/activity_add_product.xml @@ -2,13 +2,16 @@ - + - + - + - + - + - + tools:context=".ui.profile.mystore.product.ProductActivity" + android:orientation="vertical"> - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_shipping_confirmation.xml b/app/src/main/res/layout/activity_shipping_confirmation.xml index 91a3aa9..f20c38c 100644 --- a/app/src/main/res/layout/activity_shipping_confirmation.xml +++ b/app/src/main/res/layout/activity_shipping_confirmation.xml @@ -2,13 +2,16 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +