mirror of
https://github.com/shaulascr/ecommerce_serang.git
synced 2025-08-10 09:22:21 +00:00
product create, update, delete
This commit is contained in:
@ -10,6 +10,9 @@ data class Product(
|
|||||||
@field:SerializedName("image")
|
@field:SerializedName("image")
|
||||||
val image: String? = null,
|
val image: String? = null,
|
||||||
|
|
||||||
|
@field:SerializedName("is_wholesale")
|
||||||
|
val isWholesale: Boolean? = null,
|
||||||
|
|
||||||
@field:SerializedName("sppirt")
|
@field:SerializedName("sppirt")
|
||||||
val sppirt: String? = null,
|
val sppirt: String? = null,
|
||||||
|
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.alya.ecommerce_serang.data.api.dto
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
data class Wholesale(
|
||||||
|
|
||||||
|
@field:SerializedName("product_id")
|
||||||
|
val productId: Int? = null,
|
||||||
|
|
||||||
|
@field:SerializedName("wholesale_price")
|
||||||
|
val wholesalePrice: String? = null,
|
||||||
|
|
||||||
|
@field:SerializedName("id")
|
||||||
|
val id: Int? = null,
|
||||||
|
|
||||||
|
@field:SerializedName("min_item")
|
||||||
|
val minItem: Int? = null
|
||||||
|
)
|
@ -2,6 +2,7 @@ package com.alya.ecommerce_serang.data.api.response.store.product
|
|||||||
|
|
||||||
import com.alya.ecommerce_serang.data.api.dto.Preorder
|
import com.alya.ecommerce_serang.data.api.dto.Preorder
|
||||||
import com.alya.ecommerce_serang.data.api.dto.Product
|
import com.alya.ecommerce_serang.data.api.dto.Product
|
||||||
|
import com.alya.ecommerce_serang.data.api.dto.Wholesale
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
data class CreateProductResponse(
|
data class CreateProductResponse(
|
||||||
@ -9,9 +10,12 @@ data class CreateProductResponse(
|
|||||||
@field:SerializedName("product")
|
@field:SerializedName("product")
|
||||||
val product: Product? = null,
|
val product: Product? = null,
|
||||||
|
|
||||||
|
@field:SerializedName("wholesale")
|
||||||
|
val wholesale: Wholesale? = null,
|
||||||
|
|
||||||
@field:SerializedName("message")
|
@field:SerializedName("message")
|
||||||
val message: String? = null,
|
val message: String? = null,
|
||||||
|
|
||||||
@field:SerializedName("preorder")
|
@field:SerializedName("preorder")
|
||||||
val preorder: Preorder? = null
|
val preorder: Preorder? = null
|
||||||
)
|
)
|
@ -1,6 +1,8 @@
|
|||||||
package com.alya.ecommerce_serang.data.api.response.store.product
|
package com.alya.ecommerce_serang.data.api.response.store.product
|
||||||
|
|
||||||
|
import com.alya.ecommerce_serang.data.api.dto.Preorder
|
||||||
import com.alya.ecommerce_serang.data.api.dto.Product
|
import com.alya.ecommerce_serang.data.api.dto.Product
|
||||||
|
import com.alya.ecommerce_serang.data.api.dto.Wholesale
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
data class UpdateProductResponse(
|
data class UpdateProductResponse(
|
||||||
@ -8,6 +10,12 @@ data class UpdateProductResponse(
|
|||||||
@field:SerializedName("product")
|
@field:SerializedName("product")
|
||||||
val product: Product? = null,
|
val product: Product? = null,
|
||||||
|
|
||||||
|
@field:SerializedName("wholesale")
|
||||||
|
val wholesale: Wholesale? = null,
|
||||||
|
|
||||||
@field:SerializedName("message")
|
@field:SerializedName("message")
|
||||||
val message: String? = null
|
val message: String? = null,
|
||||||
|
|
||||||
|
@field:SerializedName("preorder")
|
||||||
|
val preorder: Preorder? = null
|
||||||
)
|
)
|
||||||
|
@ -263,6 +263,9 @@ interface ApiService {
|
|||||||
@Part("weight") weight: RequestBody,
|
@Part("weight") weight: RequestBody,
|
||||||
@Part("is_pre_order") isPreOrder: RequestBody,
|
@Part("is_pre_order") isPreOrder: RequestBody,
|
||||||
@Part("duration") duration: RequestBody,
|
@Part("duration") duration: RequestBody,
|
||||||
|
@Part("is_wholesale") isWholesale: RequestBody,
|
||||||
|
@Part("wholesale_min_item") minItemWholesale: RequestBody,
|
||||||
|
@Part("wholesale_price") wholesalePrice: RequestBody,
|
||||||
@Part("category_id") categoryId: RequestBody,
|
@Part("category_id") categoryId: RequestBody,
|
||||||
@Part("status") status: RequestBody,
|
@Part("status") status: RequestBody,
|
||||||
@Part("condition") condition: RequestBody,
|
@Part("condition") condition: RequestBody,
|
||||||
|
@ -6,14 +6,16 @@ import com.alya.ecommerce_serang.data.api.dto.CategoryItem
|
|||||||
import com.alya.ecommerce_serang.data.api.dto.Preorder
|
import com.alya.ecommerce_serang.data.api.dto.Preorder
|
||||||
import com.alya.ecommerce_serang.data.api.dto.ProductsItem
|
import com.alya.ecommerce_serang.data.api.dto.ProductsItem
|
||||||
import com.alya.ecommerce_serang.data.api.dto.SearchRequest
|
import com.alya.ecommerce_serang.data.api.dto.SearchRequest
|
||||||
|
import com.alya.ecommerce_serang.data.api.dto.Wholesale
|
||||||
|
import com.alya.ecommerce_serang.data.api.response.store.product.CreateProductResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.customer.cart.AddCartResponse
|
import com.alya.ecommerce_serang.data.api.response.customer.cart.AddCartResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.customer.product.ProductResponse
|
import com.alya.ecommerce_serang.data.api.response.customer.product.ProductResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.customer.product.ReviewsItem
|
import com.alya.ecommerce_serang.data.api.response.customer.product.ReviewsItem
|
||||||
import com.alya.ecommerce_serang.data.api.response.customer.product.StoreProduct
|
import com.alya.ecommerce_serang.data.api.response.customer.product.StoreProduct
|
||||||
import com.alya.ecommerce_serang.data.api.response.product.Search
|
|
||||||
import com.alya.ecommerce_serang.data.api.response.store.product.CreateProductResponse
|
|
||||||
import com.alya.ecommerce_serang.data.api.response.store.product.UpdateProductResponse
|
import com.alya.ecommerce_serang.data.api.response.store.product.UpdateProductResponse
|
||||||
|
import com.alya.ecommerce_serang.data.api.response.product.Search
|
||||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiService
|
import com.alya.ecommerce_serang.data.api.retrofit.ApiService
|
||||||
|
import com.alya.ecommerce_serang.utils.SessionManager
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||||
@ -161,6 +163,8 @@ class ProductRepository(private val apiService: ApiService) {
|
|||||||
weight: Int,
|
weight: Int,
|
||||||
isPreOrder: Boolean,
|
isPreOrder: Boolean,
|
||||||
preorder: Preorder,
|
preorder: Preorder,
|
||||||
|
isWholesale: Boolean,
|
||||||
|
wholesale: Wholesale,
|
||||||
categoryId: Int,
|
categoryId: Int,
|
||||||
status: String,
|
status: String,
|
||||||
condition: String,
|
condition: String,
|
||||||
@ -178,6 +182,9 @@ class ProductRepository(private val apiService: ApiService) {
|
|||||||
weight = RequestBody.create("text/plain".toMediaTypeOrNull(), weight.toString()),
|
weight = RequestBody.create("text/plain".toMediaTypeOrNull(), weight.toString()),
|
||||||
isPreOrder = RequestBody.create("text/plain".toMediaTypeOrNull(), isPreOrder.toString()),
|
isPreOrder = RequestBody.create("text/plain".toMediaTypeOrNull(), isPreOrder.toString()),
|
||||||
duration = RequestBody.create("text/plain".toMediaTypeOrNull(), preorder.duration.toString()),
|
duration = RequestBody.create("text/plain".toMediaTypeOrNull(), preorder.duration.toString()),
|
||||||
|
isWholesale = RequestBody.create("text/plain".toMediaTypeOrNull(), isWholesale.toString()),
|
||||||
|
minItemWholesale = RequestBody.create("text/plain".toMediaTypeOrNull(), wholesale.minItem.toString()),
|
||||||
|
wholesalePrice = RequestBody.create("text/plain".toMediaTypeOrNull(), wholesale.wholesalePrice.toString()),
|
||||||
categoryId = RequestBody.create("text/plain".toMediaTypeOrNull(), categoryId.toString()),
|
categoryId = RequestBody.create("text/plain".toMediaTypeOrNull(), categoryId.toString()),
|
||||||
status = RequestBody.create("text/plain".toMediaTypeOrNull(), status),
|
status = RequestBody.create("text/plain".toMediaTypeOrNull(), status),
|
||||||
condition = RequestBody.create("text/plain".toMediaTypeOrNull(), condition),
|
condition = RequestBody.create("text/plain".toMediaTypeOrNull(), condition),
|
||||||
|
@ -3,8 +3,10 @@ package com.alya.ecommerce_serang.ui.profile.mystore.product
|
|||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.database.Cursor
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.provider.OpenableColumns
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
@ -16,7 +18,6 @@ import androidx.appcompat.app.AppCompatActivity
|
|||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.alya.ecommerce_serang.data.api.dto.CategoryItem
|
import com.alya.ecommerce_serang.data.api.dto.CategoryItem
|
||||||
import com.alya.ecommerce_serang.data.api.dto.Preorder
|
import com.alya.ecommerce_serang.data.api.dto.Preorder
|
||||||
import com.alya.ecommerce_serang.data.api.dto.Product
|
|
||||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||||
import com.alya.ecommerce_serang.data.repository.ProductRepository
|
import com.alya.ecommerce_serang.data.repository.ProductRepository
|
||||||
import com.alya.ecommerce_serang.data.repository.Result
|
import com.alya.ecommerce_serang.data.repository.Result
|
||||||
@ -32,6 +33,9 @@ import java.io.File
|
|||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import kotlin.getValue
|
import kotlin.getValue
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
|
import androidx.core.widget.doAfterTextChanged
|
||||||
|
import com.alya.ecommerce_serang.BuildConfig.BASE_URL
|
||||||
|
import com.alya.ecommerce_serang.data.api.dto.Wholesale
|
||||||
|
|
||||||
class DetailStoreProductActivity : AppCompatActivity() {
|
class DetailStoreProductActivity : AppCompatActivity() {
|
||||||
|
|
||||||
@ -42,6 +46,7 @@ class DetailStoreProductActivity : AppCompatActivity() {
|
|||||||
private var sppirtUri: Uri? = null
|
private var sppirtUri: Uri? = null
|
||||||
private var halalUri: Uri? = null
|
private var halalUri: Uri? = null
|
||||||
private var productId: Int? = null
|
private var productId: Int? = null
|
||||||
|
private var hasImage: Boolean = false
|
||||||
|
|
||||||
private val viewModel: ProductViewModel by viewModels {
|
private val viewModel: ProductViewModel by viewModels {
|
||||||
BaseViewModelFactory {
|
BaseViewModelFactory {
|
||||||
@ -58,6 +63,7 @@ class DetailStoreProductActivity : AppCompatActivity() {
|
|||||||
imageUri?.let {
|
imageUri?.let {
|
||||||
binding.ivPreviewFoto.setImageURI(it)
|
binding.ivPreviewFoto.setImageURI(it)
|
||||||
binding.switcherFotoProduk.showNext()
|
binding.switcherFotoProduk.showNext()
|
||||||
|
hasImage = true
|
||||||
}
|
}
|
||||||
validateForm()
|
validateForm()
|
||||||
}
|
}
|
||||||
@ -89,17 +95,15 @@ class DetailStoreProductActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
binding.header.headerTitle.text = if (isEditing) "Ubah Produk" else "Tambah Produk"
|
binding.header.headerTitle.text = if (isEditing) "Ubah Produk" else "Tambah Produk"
|
||||||
|
|
||||||
// if (isEditing && productId != null) {
|
if (isEditing && productId != null && productId != -1) {
|
||||||
// viewModel.productDetail.observe(this) { product ->
|
viewModel.loadProductDetail(productId!!)
|
||||||
// product?.let {
|
viewModel.productDetail.observe(this) { product ->
|
||||||
// populateForm(it)
|
product?.let { populateForm(it) }
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// viewModel.loadProductDetail(productId!!)
|
|
||||||
// }
|
|
||||||
|
|
||||||
setupCategorySpinner()
|
setupCategorySpinner()
|
||||||
setupImagePickers()
|
setupFilePickers()
|
||||||
|
|
||||||
var conditionList = listOf("Baru", "Pernah Dipakai")
|
var conditionList = listOf("Baru", "Pernah Dipakai")
|
||||||
val adapterCondition = ArrayAdapter(this, android.R.layout.simple_spinner_item, conditionList)
|
val adapterCondition = ArrayAdapter(this, android.R.layout.simple_spinner_item, conditionList)
|
||||||
@ -108,7 +112,27 @@ class DetailStoreProductActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
// Setup Pre-Order visibility
|
// Setup Pre-Order visibility
|
||||||
binding.switchIsPreOrder.setOnCheckedChangeListener { _, isChecked ->
|
binding.switchIsPreOrder.setOnCheckedChangeListener { _, isChecked ->
|
||||||
binding.layoutDurasi.visibility = if (isChecked) View.VISIBLE else View.GONE
|
togglePreOrderVisibility(isChecked)
|
||||||
|
validateForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.switchIsWholesale.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
toggleWholesaleVisibility(isChecked)
|
||||||
|
validateForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
with(binding) {
|
||||||
|
edtNamaProduk.doAfterTextChanged { validateForm() }
|
||||||
|
edtDeskripsiProduk.doAfterTextChanged { validateForm() }
|
||||||
|
edtHargaProduk.doAfterTextChanged { validateForm() }
|
||||||
|
edtStokProduk.doAfterTextChanged { validateForm() }
|
||||||
|
edtMinOrder.doAfterTextChanged { validateForm() }
|
||||||
|
edtBeratProduk.doAfterTextChanged { validateForm() }
|
||||||
|
edtDurasi.doAfterTextChanged { validateForm() }
|
||||||
|
edtMinPesanGrosir.doAfterTextChanged { validateForm() }
|
||||||
|
edtHargaGrosir.doAfterTextChanged { validateForm() }
|
||||||
|
switchIsPreOrder.setOnCheckedChangeListener { _, _ -> validateForm() }
|
||||||
|
switchIsWholesale.setOnCheckedChangeListener { _, _ -> validateForm() }
|
||||||
}
|
}
|
||||||
|
|
||||||
validateForm()
|
validateForm()
|
||||||
@ -138,7 +162,7 @@ class DetailStoreProductActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupImagePickers() {
|
private fun setupFilePickers() {
|
||||||
binding.tvTambahFoto.setOnClickListener {
|
binding.tvTambahFoto.setOnClickListener {
|
||||||
val intent = Intent(Intent.ACTION_PICK).apply { type = "image/*" }
|
val intent = Intent(Intent.ACTION_PICK).apply { type = "image/*" }
|
||||||
imagePickerLauncher.launch(intent)
|
imagePickerLauncher.launch(intent)
|
||||||
@ -151,6 +175,7 @@ class DetailStoreProductActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
binding.btnRemoveFoto.setOnClickListener {
|
binding.btnRemoveFoto.setOnClickListener {
|
||||||
imageUri = null
|
imageUri = null
|
||||||
|
hasImage = false
|
||||||
binding.switcherFotoProduk.showPrevious()
|
binding.switcherFotoProduk.showPrevious()
|
||||||
validateForm()
|
validateForm()
|
||||||
}
|
}
|
||||||
@ -168,33 +193,56 @@ class DetailStoreProductActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun populateForm(product: Product?) {
|
private fun populateForm(product: com.alya.ecommerce_serang.data.api.response.customer.product.Product) {
|
||||||
binding.edtNamaProduk.setText(product?.name)
|
binding.edtNamaProduk.setText(product.productName)
|
||||||
binding.edtDeskripsiProduk.setText(product?.description)
|
binding.edtDeskripsiProduk.setText(product.description)
|
||||||
binding.edtHargaProduk.setText(product?.price.toString())
|
binding.edtHargaProduk.setText(product.price.toString())
|
||||||
binding.edtStokProduk.setText(product?.stock.toString())
|
binding.edtStokProduk.setText(product.stock.toString())
|
||||||
binding.edtMinOrder.setText(product?.minOrder.toString())
|
binding.edtMinOrder.setText(product.minOrder.toString())
|
||||||
binding.edtBeratProduk.setText(product?.weight.toString())
|
binding.edtBeratProduk.setText(product.weight.toString())
|
||||||
binding.switchIsPreOrder.isChecked = product?.isPreOrder == false
|
binding.spinnerKondisiProduk.setSelection(if (product.condition == "Baru") 0 else 1)
|
||||||
// binding.switchIsWholesale.isChecked = product?.isWholesale == false
|
|
||||||
// binding.edtMinPesanGrosir.setText(product?.minOrderWholesale.toString())
|
|
||||||
// binding.edtHargaGrosir.setText(product?.priceWholesale.toString())
|
|
||||||
binding.switchIsActive.isChecked = product?.status == "active"
|
|
||||||
binding.spinnerKondisiProduk.setSelection(if (product?.condition == "Baru") 0 else 1)
|
|
||||||
|
|
||||||
product?.categoryId?.let {
|
// Category selection
|
||||||
binding.spinnerKategoriProduk.setSelection(categoryList.indexOfFirst { it.id == product.categoryId })
|
product.categoryId.let { categoryId ->
|
||||||
|
val index = categoryList.indexOfFirst { it.id == categoryId }
|
||||||
|
if (index != -1) binding.spinnerKategoriProduk.setSelection(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
Glide.with(this).load(product?.image).into(binding.ivPreviewFoto)
|
// Pre-order
|
||||||
binding.switcherFotoProduk.showNext()
|
val isPreOrder = product.isPreOrder == true
|
||||||
|
binding.switchIsPreOrder.jumpDrawablesToCurrentState()
|
||||||
|
binding.switchIsPreOrder.isChecked = isPreOrder
|
||||||
|
togglePreOrderVisibility(isPreOrder)
|
||||||
|
if (isPreOrder) {
|
||||||
|
binding.edtDurasi.setText(product.preorderDuration?.toString() ?: "")
|
||||||
|
}
|
||||||
|
|
||||||
product?.sppirt?.let {
|
// Wholesale
|
||||||
|
val isWholesale = product.isWholesale == true
|
||||||
|
binding.switchIsWholesale.jumpDrawablesToCurrentState()
|
||||||
|
binding.switchIsWholesale.isChecked = isWholesale
|
||||||
|
toggleWholesaleVisibility(isWholesale)
|
||||||
|
if (isWholesale) {
|
||||||
|
binding.edtMinPesanGrosir.setText(product.wholesaleMinItem?.toString() ?: "")
|
||||||
|
binding.edtHargaGrosir.setText(product.wholesalePrice?.toString() ?: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Product image
|
||||||
|
val imageUrl = if (product.image.startsWith("/")) {
|
||||||
|
BASE_URL + product.image.removePrefix("/")
|
||||||
|
} else product.image
|
||||||
|
Glide.with(this).load(imageUrl).into(binding.ivPreviewFoto)
|
||||||
|
binding.switcherFotoProduk.showNext()
|
||||||
|
hasImage = true
|
||||||
|
|
||||||
|
// SPPIRT
|
||||||
|
product.sppirt?.let {
|
||||||
binding.tvSppirtName.text = getFileName(it.toUri())
|
binding.tvSppirtName.text = getFileName(it.toUri())
|
||||||
binding.switcherSppirt.showNext()
|
binding.switcherSppirt.showNext()
|
||||||
}
|
}
|
||||||
|
|
||||||
product?.halal?.let {
|
// Halal
|
||||||
|
product.halal?.let {
|
||||||
binding.tvHalalName.text = getFileName(it.toUri())
|
binding.tvHalalName.text = getFileName(it.toUri())
|
||||||
binding.switcherHalal.showNext()
|
binding.switcherHalal.showNext()
|
||||||
}
|
}
|
||||||
@ -202,13 +250,42 @@ class DetailStoreProductActivity : AppCompatActivity() {
|
|||||||
validateForm()
|
validateForm()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun togglePreOrderVisibility(isChecked: Boolean) {
|
||||||
|
Log.d("DEBUG", "togglePreOrderVisibility: $isChecked")
|
||||||
|
binding.layoutDurasi.visibility = if (isChecked) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toggleWholesaleVisibility(isChecked: Boolean) {
|
||||||
|
Log.d("DEBUG", "toggleWholesaleVisibility: $isChecked")
|
||||||
|
binding.layoutMinPesanGrosir.visibility = if (isChecked) View.VISIBLE else View.GONE
|
||||||
|
binding.layoutHargaGrosir.visibility = if (isChecked) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
private fun isValidFile(uri: Uri): Boolean {
|
private fun isValidFile(uri: Uri): Boolean {
|
||||||
val mimeType = contentResolver.getType(uri) ?: return false
|
val mimeType = contentResolver.getType(uri) ?: return false
|
||||||
return listOf("application/pdf", "image/jpeg", "image/png", "image/jpg").contains(mimeType)
|
return listOf("application/pdf", "image/jpeg", "image/png", "image/jpg").contains(mimeType)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getFileName(uri: Uri): String {
|
private fun getFileName(uri: Uri): String {
|
||||||
return uri.lastPathSegment?.split("/")?.last() ?: "unknown_file"
|
var name = "unknown_file"
|
||||||
|
val cursor = contentResolver.query(uri, null, null, null, null)
|
||||||
|
cursor?.use {
|
||||||
|
if (it.moveToFirst()) {
|
||||||
|
val nameIndex = it.getColumnIndexOpenableColumnsDisplayName()
|
||||||
|
if (nameIndex != -1) {
|
||||||
|
name = it.getString(nameIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Cursor.getColumnIndexOpenableColumnsDisplayName(): Int {
|
||||||
|
return try {
|
||||||
|
getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
-1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun uriToNamedFile(uri: Uri, context: Context, prefix: String): File {
|
private fun uriToNamedFile(uri: Uri, context: Context, prefix: String): File {
|
||||||
@ -242,36 +319,63 @@ class DetailStoreProductActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun validateForm() {
|
private fun validateForm() {
|
||||||
val valid = binding.edtNamaProduk.text.isNotBlank() &&
|
val name = binding.edtNamaProduk.text.toString().trim()
|
||||||
binding.edtDeskripsiProduk.text.isNotBlank() &&
|
val description = binding.edtDeskripsiProduk.text.toString().trim()
|
||||||
binding.edtHargaProduk.text.isNotBlank() &&
|
val price = binding.edtHargaProduk.text.toString().trim()
|
||||||
binding.edtStokProduk.text.isNotBlank() &&
|
val stock = binding.edtStokProduk.text.toString().trim()
|
||||||
binding.edtMinOrder.text.isNotBlank() &&
|
val minOrder = binding.edtMinOrder.text.toString().trim()
|
||||||
binding.edtBeratProduk.text.isNotBlank() &&
|
val weight = binding.edtBeratProduk.text.toString().trim()
|
||||||
(!binding.switchIsPreOrder.isChecked || binding.edtDurasi.text.isNotBlank()) &&
|
val duration = binding.edtDurasi.text.toString().trim()
|
||||||
imageUri != null
|
val wholesaleMinItem = binding.edtMinPesanGrosir.text.toString().trim()
|
||||||
|
val wholesalePrice = binding.edtHargaGrosir.text.toString().trim()
|
||||||
|
val category = binding.spinnerKategoriProduk.selectedItemPosition != -1
|
||||||
|
val isPreOrderChecked = binding.switchIsPreOrder.isChecked
|
||||||
|
val isWholesaleChecked = binding.switchIsWholesale.isChecked
|
||||||
|
|
||||||
|
val valid = name.isNotEmpty() &&
|
||||||
|
description.isNotEmpty() &&
|
||||||
|
price.isNotEmpty() &&
|
||||||
|
stock.isNotEmpty() &&
|
||||||
|
minOrder.isNotEmpty() &&
|
||||||
|
weight.isNotEmpty() &&
|
||||||
|
(!isPreOrderChecked || duration.isNotEmpty()) &&
|
||||||
|
(!isWholesaleChecked || (wholesaleMinItem.isNotEmpty() && wholesalePrice.isNotEmpty())) &&
|
||||||
|
category &&
|
||||||
|
hasImage
|
||||||
|
|
||||||
binding.btnSaveProduct.isEnabled = valid
|
binding.btnSaveProduct.isEnabled = valid
|
||||||
binding.btnSaveProduct.setTextColor(
|
binding.btnSaveProduct.setTextColor(
|
||||||
if (valid) ContextCompat.getColor(this, R.color.white) else ContextCompat.getColor(this, R.color.black_300)
|
if (valid) ContextCompat.getColor(this, R.color.white)
|
||||||
|
else ContextCompat.getColor(this, R.color.black_300)
|
||||||
)
|
)
|
||||||
binding.btnSaveProduct.setBackgroundResource(
|
binding.btnSaveProduct.setBackgroundResource(
|
||||||
if (valid) R.drawable.bg_button_active else R.drawable.bg_button_disabled
|
if (valid) R.drawable.bg_button_active
|
||||||
|
else R.drawable.bg_button_disabled
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addProduct() {
|
private fun addProduct() {
|
||||||
val name = binding.edtNamaProduk.text.toString()
|
val name = binding.edtNamaProduk.text.toString().trim()
|
||||||
val description = binding.edtDeskripsiProduk.text.toString()
|
val description = binding.edtDeskripsiProduk.text.toString().trim()
|
||||||
val price = binding.edtHargaProduk.text.toString().toInt()
|
val price = binding.edtHargaProduk.text.toString().toIntOrNull() ?: return showInputError("Harga produk")
|
||||||
val stock = binding.edtStokProduk.text.toString().toInt()
|
val stock = binding.edtStokProduk.text.toString().toIntOrNull() ?: return showInputError("Stok produk")
|
||||||
val minOrder = binding.edtMinOrder.text.toString().toInt()
|
val minOrder = binding.edtMinOrder.text.toString().toIntOrNull() ?: return showInputError("Minimal order")
|
||||||
val weight = binding.edtBeratProduk.text.toString().toInt()
|
val weight = binding.edtBeratProduk.text.toString().toIntOrNull() ?: return showInputError("Berat produk")
|
||||||
|
|
||||||
val isPreOrder = binding.switchIsPreOrder.isChecked
|
val isPreOrder = binding.switchIsPreOrder.isChecked
|
||||||
val duration = if (isPreOrder) binding.edtDurasi.text.toString().toInt() else 0
|
val duration = if (isPreOrder) {
|
||||||
|
binding.edtDurasi.text.toString().toIntOrNull() ?: return showInputError("Durasi pre-order")
|
||||||
|
} else 0
|
||||||
|
|
||||||
val isWholesale = binding.switchIsWholesale.isChecked
|
val isWholesale = binding.switchIsWholesale.isChecked
|
||||||
val minOrderWholesale = binding.edtMinPesanGrosir.text.toString().toInt()
|
val minOrderWholesale = if (isWholesale) {
|
||||||
val priceWholesale = binding.edtHargaGrosir.text.toString().toInt()
|
binding.edtMinPesanGrosir.text.toString().toIntOrNull() ?: return showInputError("Min. grosir")
|
||||||
|
} else 0
|
||||||
|
|
||||||
|
val priceWholesale = if (isWholesale) {
|
||||||
|
binding.edtHargaGrosir.text.toString().toIntOrNull() ?: return showInputError("Harga grosir")
|
||||||
|
} else 0
|
||||||
|
|
||||||
val status = if (binding.switchIsActive.isChecked) "active" else "inactive"
|
val status = if (binding.switchIsActive.isChecked) "active" else "inactive"
|
||||||
val condition = binding.spinnerKondisiProduk.selectedItem.toString()
|
val condition = binding.spinnerKondisiProduk.selectedItem.toString()
|
||||||
val categoryId = categoryList.getOrNull(binding.spinnerKategoriProduk.selectedItemPosition)?.id ?: 0
|
val categoryId = categoryList.getOrNull(binding.spinnerKategoriProduk.selectedItemPosition)?.id ?: 0
|
||||||
@ -288,9 +392,14 @@ class DetailStoreProductActivity : AppCompatActivity() {
|
|||||||
val halalPart = halalFile?.let { createPartFromFile("halal", it) }
|
val halalPart = halalFile?.let { createPartFromFile("halal", it) }
|
||||||
|
|
||||||
val preorder = Preorder(productId = productId, duration = duration)
|
val preorder = Preorder(productId = productId, duration = duration)
|
||||||
|
val wholesale = Wholesale(productId = productId, minItem = minOrderWholesale, wholesalePrice = priceWholesale.toString())
|
||||||
|
|
||||||
viewModel.addProduct(
|
viewModel.addProduct(
|
||||||
name, description, price, stock, minOrder, weight, isPreOrder, preorder, categoryId, status, condition, imagePart, sppirtPart, halalPart
|
name, description, price, stock, minOrder, weight,
|
||||||
|
isPreOrder, preorder,
|
||||||
|
isWholesale, wholesale,
|
||||||
|
categoryId, status, condition,
|
||||||
|
imagePart, sppirtPart, halalPart
|
||||||
)
|
)
|
||||||
|
|
||||||
viewModel.productCreationResult.observe(this) { result ->
|
viewModel.productCreationResult.observe(this) { result ->
|
||||||
@ -309,6 +418,11 @@ class DetailStoreProductActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showInputError(fieldName: String): Nothing {
|
||||||
|
Toast.makeText(this, "$fieldName tidak boleh kosong dan harus berupa angka.", Toast.LENGTH_SHORT).show()
|
||||||
|
return throw IllegalArgumentException("$fieldName invalid")
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateProduct(productId: Int?) {
|
private fun updateProduct(productId: Int?) {
|
||||||
val updatedProduct = mapOf(
|
val updatedProduct = mapOf(
|
||||||
"name" to binding.edtNamaProduk.text.toString(),
|
"name" to binding.edtNamaProduk.text.toString(),
|
||||||
@ -318,7 +432,10 @@ class DetailStoreProductActivity : AppCompatActivity() {
|
|||||||
"min_order" to binding.edtMinOrder.text.toString().toInt(),
|
"min_order" to binding.edtMinOrder.text.toString().toInt(),
|
||||||
"weight" to binding.edtBeratProduk.text.toString().toInt(),
|
"weight" to binding.edtBeratProduk.text.toString().toInt(),
|
||||||
"is_pre_order" to binding.switchIsPreOrder.isChecked,
|
"is_pre_order" to binding.switchIsPreOrder.isChecked,
|
||||||
"duration" to binding.edtDurasi.text.toString().toInt(),
|
"duration" to (binding.edtDurasi.text.toString().toIntOrNull() ?: 0),
|
||||||
|
"is_wholesale" to binding.switchIsWholesale.isChecked,
|
||||||
|
"wholesale_min_item" to (binding.edtMinPesanGrosir.text.toString().toIntOrNull() ?: 0),
|
||||||
|
"wholesale_price" to (binding.edtHargaGrosir.text.toString().toIntOrNull() ?: 0),
|
||||||
"category_id" to categoryList[binding.spinnerKategoriProduk.selectedItemPosition].id,
|
"category_id" to categoryList[binding.spinnerKategoriProduk.selectedItemPosition].id,
|
||||||
"status" to if (binding.switchIsActive.isChecked) "active" else "inactive",
|
"status" to if (binding.switchIsActive.isChecked) "active" else "inactive",
|
||||||
"condition" to binding.spinnerKondisiProduk.selectedItem.toString(),
|
"condition" to binding.spinnerKondisiProduk.selectedItem.toString(),
|
||||||
@ -328,5 +445,20 @@ class DetailStoreProductActivity : AppCompatActivity() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
viewModel.updateProduct(productId, updatedProduct)
|
viewModel.updateProduct(productId, updatedProduct)
|
||||||
|
viewModel.productUpdateResult.observe(this) { result ->
|
||||||
|
when (result) {
|
||||||
|
is Result.Loading -> binding.btnSaveProduct.isEnabled = false
|
||||||
|
is Result.Success -> {
|
||||||
|
val product = result.data.product
|
||||||
|
Toast.makeText(this, "Produk berhasil diubah: ${product?.name}", Toast.LENGTH_SHORT).show()
|
||||||
|
setResult(Activity.RESULT_OK)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
is Result.Error -> {
|
||||||
|
Log.e("ProductDetailActivity", "Error: ${result.exception.message}")
|
||||||
|
binding.btnSaveProduct.isEnabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -43,6 +43,11 @@ class ProductActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
viewModel.loadMyStoreProducts()
|
||||||
|
}
|
||||||
|
|
||||||
private fun observeViewModel() {
|
private fun observeViewModel() {
|
||||||
viewModel.productList.observe(this) { result ->
|
viewModel.productList.observe(this) { result ->
|
||||||
when (result) {
|
when (result) {
|
||||||
|
@ -10,6 +10,7 @@ import android.widget.Toast
|
|||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.alya.ecommerce_serang.BuildConfig.BASE_URL
|
||||||
import com.alya.ecommerce_serang.R
|
import com.alya.ecommerce_serang.R
|
||||||
import com.alya.ecommerce_serang.data.api.dto.Product
|
import com.alya.ecommerce_serang.data.api.dto.Product
|
||||||
import com.alya.ecommerce_serang.data.api.dto.ProductsItem
|
import com.alya.ecommerce_serang.data.api.dto.ProductsItem
|
||||||
@ -43,8 +44,13 @@ class ProductAdapter(
|
|||||||
tvProductStatus.background = ContextCompat.getDrawable(itemView.context, R.drawable.bg_product_inactive)
|
tvProductStatus.background = ContextCompat.getDrawable(itemView.context, R.drawable.bg_product_inactive)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val imageUrl = if (product.image.startsWith("/")) {
|
||||||
|
BASE_URL + product.image.removePrefix("/") // Append base URL if the path starts with "/"
|
||||||
|
} else {
|
||||||
|
product.image // Use as is if it's already a full URL
|
||||||
|
}
|
||||||
Glide.with(itemView.context)
|
Glide.with(itemView.context)
|
||||||
.load(product.image)
|
.load(imageUrl)
|
||||||
.placeholder(R.drawable.placeholder_image)
|
.placeholder(R.drawable.placeholder_image)
|
||||||
.into(ivProduct)
|
.into(ivProduct)
|
||||||
|
|
||||||
|
@ -42,10 +42,14 @@ class ProductOptionsBottomSheetFragment(private val product: ProductsItem) : Bot
|
|||||||
.setTitle("Hapus Produk?")
|
.setTitle("Hapus Produk?")
|
||||||
.setMessage("Produk yang dihapus tidak dapat dikembalikan.")
|
.setMessage("Produk yang dihapus tidak dapat dikembalikan.")
|
||||||
.setPositiveButton("Ya, Hapus") { _, _ ->
|
.setPositiveButton("Ya, Hapus") { _, _ ->
|
||||||
val viewModel = ViewModelProvider(this).get(ProductViewModel::class.java)
|
val viewModel = ViewModelProvider(requireActivity())[ProductViewModel::class.java]
|
||||||
viewModel.deleteProduct(product.id)
|
viewModel.deleteProduct(product.id)
|
||||||
Toast.makeText(context, "Produk berhasil dihapus", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "Produk berhasil dihapus", Toast.LENGTH_SHORT).show()
|
||||||
dismiss()
|
dismiss()
|
||||||
|
activity?.run {
|
||||||
|
finish()
|
||||||
|
startActivity(Intent(this, ProductActivity::class.java))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.setNegativeButton("Batalkan", null)
|
.setNegativeButton("Batalkan", null)
|
||||||
.show()
|
.show()
|
||||||
|
@ -7,6 +7,7 @@ import androidx.lifecycle.viewModelScope
|
|||||||
import com.alya.ecommerce_serang.data.api.dto.CategoryItem
|
import com.alya.ecommerce_serang.data.api.dto.CategoryItem
|
||||||
import com.alya.ecommerce_serang.data.api.dto.Preorder
|
import com.alya.ecommerce_serang.data.api.dto.Preorder
|
||||||
import com.alya.ecommerce_serang.data.api.dto.ProductsItem
|
import com.alya.ecommerce_serang.data.api.dto.ProductsItem
|
||||||
|
import com.alya.ecommerce_serang.data.api.dto.Wholesale
|
||||||
import com.alya.ecommerce_serang.data.api.response.store.product.CreateProductResponse
|
import com.alya.ecommerce_serang.data.api.response.store.product.CreateProductResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.customer.product.Product
|
import com.alya.ecommerce_serang.data.api.response.customer.product.Product
|
||||||
import com.alya.ecommerce_serang.data.api.response.customer.product.ReviewsItem
|
import com.alya.ecommerce_serang.data.api.response.customer.product.ReviewsItem
|
||||||
@ -88,6 +89,8 @@ class ProductViewModel(private val repository: ProductRepository) : ViewModel()
|
|||||||
weight: Int,
|
weight: Int,
|
||||||
isPreOrder: Boolean,
|
isPreOrder: Boolean,
|
||||||
preorder: Preorder,
|
preorder: Preorder,
|
||||||
|
isWholesale: Boolean,
|
||||||
|
wholesale: Wholesale,
|
||||||
categoryId: Int,
|
categoryId: Int,
|
||||||
status: String,
|
status: String,
|
||||||
condition: String,
|
condition: String,
|
||||||
@ -98,7 +101,7 @@ class ProductViewModel(private val repository: ProductRepository) : ViewModel()
|
|||||||
_productCreationResult.value = Result.Loading
|
_productCreationResult.value = Result.Loading
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val result = repository.addProduct(
|
val result = repository.addProduct(
|
||||||
name, description, price, stock, minOrder, weight, isPreOrder, preorder, categoryId, status, condition, imagePart, sppirtPart, halalPart
|
name, description, price, stock, minOrder, weight, isPreOrder, preorder, isWholesale, wholesale, categoryId, status, condition, imagePart, sppirtPart, halalPart
|
||||||
)
|
)
|
||||||
_productCreationResult.value = result
|
_productCreationResult.value = result
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[versions]
|
[versions]
|
||||||
agp = "8.9.2"
|
agp = "8.10.0"
|
||||||
glide = "4.16.0"
|
glide = "4.16.0"
|
||||||
gson = "2.11.0"
|
gson = "2.11.0"
|
||||||
hiltAndroid = "2.56.2" # Updated from 2.44 for better compatibility
|
hiltAndroid = "2.56.2" # Updated from 2.44 for better compatibility
|
||||||
|
Reference in New Issue
Block a user