mirror of
https://github.com/shaulascr/ecommerce_serang.git
synced 2025-08-10 17:32:22 +00:00
product
This commit is contained in:
@ -14,13 +14,17 @@ 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 okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
import retrofit2.Call
|
||||
import retrofit2.Response
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.Field
|
||||
import retrofit2.http.FormUrlEncoded
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Multipart
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Part
|
||||
import retrofit2.http.Path
|
||||
|
||||
interface ApiService {
|
||||
@ -74,19 +78,22 @@ interface ApiService {
|
||||
@GET("category")
|
||||
fun getCategories(): Call<CategoryResponse>
|
||||
|
||||
@Multipart
|
||||
@POST("store/createproduct")
|
||||
@FormUrlEncoded
|
||||
suspend fun addProduct(
|
||||
@Field("name") name: String,
|
||||
@Field("description") description: String,
|
||||
@Field("price") price: Int,
|
||||
@Field("stock") stock: Int,
|
||||
@Field("min_order") minOrder: Int,
|
||||
@Field("weight") weight: Int,
|
||||
@Field("is_pre_order") isPreOrder: Boolean,
|
||||
@Field("duration") duration: Int,
|
||||
@Field("category_id") categoryId: Int,
|
||||
@Field("is_active") isActive: String
|
||||
@Part("name") name: RequestBody,
|
||||
@Part("description") description: RequestBody,
|
||||
@Part("price") price: RequestBody,
|
||||
@Part("stock") stock: RequestBody,
|
||||
@Part("min_order") minOrder: RequestBody,
|
||||
@Part("weight") weight: RequestBody,
|
||||
@Part("is_pre_order") isPreOrder: RequestBody,
|
||||
@Part("duration") duration: RequestBody,
|
||||
@Part("category_id") categoryId: RequestBody,
|
||||
@Part("is_active") isActive: RequestBody,
|
||||
@Part image: MultipartBody.Part?,
|
||||
@Part sppirt: MultipartBody.Part?,
|
||||
@Part halal: MultipartBody.Part?
|
||||
): Response<Unit>
|
||||
|
||||
}
|
@ -8,6 +8,10 @@ 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
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
import java.io.File
|
||||
|
||||
class ProductRepository(private val apiService: ApiService) {
|
||||
suspend fun getAllProducts(): Result<List<ProductsItem>> =
|
||||
@ -81,12 +85,16 @@ class ProductRepository(private val apiService: ApiService) {
|
||||
}
|
||||
|
||||
suspend fun fetchMyStoreProducts(): List<ProductsItem> {
|
||||
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()}")
|
||||
return try {
|
||||
val response = apiService.getStoreProduct()
|
||||
if (response.isSuccessful) {
|
||||
response.body()?.products?.filterNotNull() ?: emptyList()
|
||||
} else {
|
||||
throw Exception("Failed to fetch store products: ${response.message()}")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("ProductRepository", "Error fetching store products", e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,33 +108,50 @@ class ProductRepository(private val apiService: ApiService) {
|
||||
isPreOrder: Boolean,
|
||||
duration: Int,
|
||||
categoryId: Int,
|
||||
isActive: Boolean
|
||||
isActive: Boolean,
|
||||
image: File?,
|
||||
sppirt: File?,
|
||||
halal: File?
|
||||
): Result<Unit> = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val status = if (isActive) "active" else "inactive"
|
||||
val namePart = RequestBody.create("text/plain".toMediaTypeOrNull(), name)
|
||||
val descriptionPart = RequestBody.create("text/plain".toMediaTypeOrNull(), description)
|
||||
val pricePart = RequestBody.create("text/plain".toMediaTypeOrNull(), price.toString())
|
||||
val stockPart = RequestBody.create("text/plain".toMediaTypeOrNull(), stock.toString())
|
||||
val minOrderPart = RequestBody.create("text/plain".toMediaTypeOrNull(), minOrder.toString())
|
||||
val weightPart = RequestBody.create("text/plain".toMediaTypeOrNull(), weight.toString())
|
||||
val isPreOrderPart = RequestBody.create("text/plain".toMediaTypeOrNull(), isPreOrder.toString())
|
||||
val durationPart = RequestBody.create("text/plain".toMediaTypeOrNull(), duration.toString())
|
||||
val categoryIdPart = RequestBody.create("text/plain".toMediaTypeOrNull(), categoryId.toString())
|
||||
val isActivePart = RequestBody.create("text/plain".toMediaTypeOrNull(), if (isActive) "1" else "0")
|
||||
|
||||
val imagePart = image?.let {
|
||||
val req = RequestBody.create("image/*".toMediaTypeOrNull(), it)
|
||||
MultipartBody.Part.createFormData("image", it.name, req)
|
||||
}
|
||||
|
||||
val sppirtPart = sppirt?.let {
|
||||
val req = RequestBody.create("application/pdf".toMediaTypeOrNull(), it)
|
||||
MultipartBody.Part.createFormData("sppirt", it.name, req)
|
||||
}
|
||||
|
||||
val halalPart = halal?.let {
|
||||
val req = RequestBody.create("application/pdf".toMediaTypeOrNull(), it)
|
||||
MultipartBody.Part.createFormData("halal", it.name, req)
|
||||
}
|
||||
|
||||
val response = apiService.addProduct(
|
||||
name = name,
|
||||
description = description,
|
||||
price = price,
|
||||
stock = stock,
|
||||
minOrder = minOrder,
|
||||
weight = weight,
|
||||
isPreOrder = isPreOrder,
|
||||
duration = duration,
|
||||
categoryId = categoryId,
|
||||
isActive = status
|
||||
namePart, descriptionPart, pricePart, stockPart, minOrderPart,
|
||||
weightPart, isPreOrderPart, durationPart, categoryIdPart, isActivePart,
|
||||
imagePart, sppirtPart, halalPart
|
||||
)
|
||||
|
||||
if (response.isSuccessful) {
|
||||
Result.Success(Unit)
|
||||
} else {
|
||||
Result.Error(Exception("Failed to add product. Code: ${response.code()}"))
|
||||
}
|
||||
if (response.isSuccessful) Result.Success(Unit)
|
||||
else Result.Error(Exception("Failed: ${response.code()}"))
|
||||
} catch (e: Exception) {
|
||||
Result.Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// suspend fun fetchStoreDetail(storeId: Int): Store? {
|
||||
|
@ -1,11 +1,16 @@
|
||||
package com.alya.ecommerce_serang.ui.profile.mystore.product
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.View
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import com.alya.ecommerce_serang.R
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
@ -18,6 +23,11 @@ import com.alya.ecommerce_serang.databinding.ActivityStoreProductDetailBinding
|
||||
import com.alya.ecommerce_serang.utils.viewmodel.ProductViewModel
|
||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||
import com.alya.ecommerce_serang.utils.SessionManager
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
import java.io.File
|
||||
import kotlin.getValue
|
||||
|
||||
class StoreProductDetailActivity : AppCompatActivity() {
|
||||
@ -25,6 +35,9 @@ class StoreProductDetailActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityStoreProductDetailBinding
|
||||
private lateinit var sessionManager: SessionManager
|
||||
private lateinit var categoryList: List<CategoryItem>
|
||||
private var imageUri: Uri? = null
|
||||
private var sppirtUri: Uri? = null
|
||||
private var halalUri: Uri? = null
|
||||
|
||||
private val viewModel: ProductViewModel by viewModels {
|
||||
BaseViewModelFactory {
|
||||
@ -35,14 +48,50 @@ class StoreProductDetailActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private val imagePickerLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
if (result.resultCode == Activity.RESULT_OK) {
|
||||
imageUri = result.data?.data
|
||||
imageUri?.let {
|
||||
binding.ivPreviewFoto.setImageURI(it)
|
||||
binding.switcherFotoProduk.showNext()
|
||||
}
|
||||
validateForm()
|
||||
}
|
||||
}
|
||||
|
||||
private val sppirtLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
|
||||
if (uri != null && isValidFile(uri)) {
|
||||
sppirtUri = uri
|
||||
binding.tvSppirtName.text = File(uri.path ?: "").name
|
||||
binding.switcherSppirt.showNext()
|
||||
}
|
||||
}
|
||||
|
||||
private val halalLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
|
||||
if (uri != null && isValidFile(uri)) {
|
||||
halalUri = uri
|
||||
binding.tvHalalName.text = File(uri.path ?: "").name
|
||||
binding.switcherHalal.showNext()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityStoreProductDetailBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
setupHeader()
|
||||
observeCategories()
|
||||
|
||||
// Fetch categories
|
||||
viewModel.loadCategories()
|
||||
viewModel.categoryList.observe(this) { result ->
|
||||
if (result is Result.Success) {
|
||||
categoryList = result.data
|
||||
val adapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, categoryList.map { it.name })
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||
binding.spinnerKategoriProduk.adapter = adapter
|
||||
}
|
||||
}
|
||||
|
||||
// Setup Pre-Order visibility
|
||||
binding.switchIsPreOrder.setOnCheckedChangeListener { _, isChecked ->
|
||||
@ -50,71 +99,123 @@ class StoreProductDetailActivity : AppCompatActivity() {
|
||||
validateForm()
|
||||
}
|
||||
|
||||
setupFormValidation()
|
||||
binding.tvTambahFoto.setOnClickListener {
|
||||
val intent = Intent(Intent.ACTION_PICK).apply { type = "image/*" }
|
||||
imagePickerLauncher.launch(intent)
|
||||
}
|
||||
|
||||
binding.layoutUploadFoto.setOnClickListener {
|
||||
val intent = Intent(Intent.ACTION_PICK).apply { type = "image/*" }
|
||||
imagePickerLauncher.launch(intent)
|
||||
}
|
||||
|
||||
binding.layoutUploadSppirt.setOnClickListener { sppirtLauncher.launch("*/*") }
|
||||
binding.btnRemoveSppirt.setOnClickListener {
|
||||
sppirtUri = null
|
||||
binding.switcherSppirt.showPrevious()
|
||||
}
|
||||
|
||||
binding.layoutUploadHalal.setOnClickListener { halalLauncher.launch("*/*") }
|
||||
binding.btnRemoveHalal.setOnClickListener {
|
||||
halalUri = null
|
||||
binding.switcherHalal.showPrevious()
|
||||
}
|
||||
|
||||
binding.btnRemoveFoto.setOnClickListener {
|
||||
imageUri = null
|
||||
binding.switcherFotoProduk.showPrevious()
|
||||
validateForm()
|
||||
}
|
||||
|
||||
val watcher = object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
||||
override fun afterTextChanged(s: Editable?) { validateForm() }
|
||||
}
|
||||
|
||||
listOf(
|
||||
binding.edtNamaProduk,
|
||||
binding.edtDeskripsiProduk,
|
||||
binding.edtHargaProduk,
|
||||
binding.edtStokProduk,
|
||||
binding.edtMinOrder,
|
||||
binding.edtBeratProduk,
|
||||
binding.edtDurasi
|
||||
).forEach { it.addTextChangedListener(watcher) }
|
||||
|
||||
validateForm()
|
||||
|
||||
binding.btnSaveProduct.setOnClickListener {
|
||||
if (binding.btnSaveProduct.isEnabled) addProduct()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupHeader() {
|
||||
binding.header.headerTitle.text = "Tambah Produk"
|
||||
|
||||
binding.header.headerLeftIcon.setOnClickListener {
|
||||
onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeCategories() {
|
||||
viewModel.categoryList.observe(this) { result ->
|
||||
when (result) {
|
||||
is Result.Loading -> {
|
||||
// Optionally show loading spinner
|
||||
}
|
||||
is Result.Success -> {
|
||||
categoryList = result.data
|
||||
setupCategorySpinner(categoryList)
|
||||
}
|
||||
is Result.Error -> {
|
||||
Toast.makeText(
|
||||
this,
|
||||
"Failed to load categories: ${result.exception.message}",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
if (!binding.btnSaveProduct.isEnabled) {
|
||||
focusFirstInvalidField()
|
||||
return@setOnClickListener
|
||||
}
|
||||
submitProduct()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupCategorySpinner(categories: List<CategoryItem>) {
|
||||
val categoryNames = categories.map { it.name }
|
||||
val adapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, categoryNames)
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||
|
||||
binding.spinnerKategoriProduk.adapter = adapter
|
||||
private fun isValidFile(uri: Uri): Boolean {
|
||||
val mimeType = contentResolver.getType(uri) ?: return false
|
||||
return listOf("application/pdf", "image/jpeg", "image/png", "image/jpg").contains(mimeType)
|
||||
}
|
||||
|
||||
private fun addProduct() {
|
||||
private fun validateForm() {
|
||||
val valid = binding.edtNamaProduk.text.isNotBlank() &&
|
||||
binding.edtDeskripsiProduk.text.isNotBlank() &&
|
||||
binding.edtHargaProduk.text.isNotBlank() &&
|
||||
binding.edtStokProduk.text.isNotBlank() &&
|
||||
binding.edtMinOrder.text.isNotBlank() &&
|
||||
binding.edtBeratProduk.text.isNotBlank() &&
|
||||
(!binding.switchIsPreOrder.isChecked || binding.edtDurasi.text.isNotBlank()) &&
|
||||
imageUri != null
|
||||
|
||||
binding.btnSaveProduct.isEnabled = valid
|
||||
binding.btnSaveProduct.setTextColor(
|
||||
if (valid) ContextCompat.getColor(this, R.color.white) else ContextCompat.getColor(this, R.color.black_300)
|
||||
)
|
||||
binding.btnSaveProduct.setBackgroundResource(
|
||||
if (valid) R.drawable.bg_button_active else R.drawable.bg_button_disabled
|
||||
)
|
||||
}
|
||||
|
||||
private fun focusFirstInvalidField() {
|
||||
when {
|
||||
binding.edtNamaProduk.text.isBlank() -> binding.edtNamaProduk.requestFocus()
|
||||
binding.edtDeskripsiProduk.text.isBlank() -> binding.edtDeskripsiProduk.requestFocus()
|
||||
binding.edtHargaProduk.text.isBlank() -> binding.edtHargaProduk.requestFocus()
|
||||
binding.edtStokProduk.text.isBlank() -> binding.edtStokProduk.requestFocus()
|
||||
binding.edtMinOrder.text.isBlank() -> binding.edtMinOrder.requestFocus()
|
||||
binding.edtBeratProduk.text.isBlank() -> binding.edtBeratProduk.requestFocus()
|
||||
binding.switchIsPreOrder.isChecked && binding.edtDurasi.text.isBlank() -> binding.edtDurasi.requestFocus()
|
||||
imageUri == null -> Toast.makeText(this, "Silakan unggah foto produk", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun uriToFile(uri: Uri, context: Context): File {
|
||||
val inputStream = context.contentResolver.openInputStream(uri)
|
||||
val file = File.createTempFile("upload_", ".tmp", context.cacheDir)
|
||||
inputStream?.use { input -> file.outputStream().use { input.copyTo(it) } }
|
||||
return file
|
||||
}
|
||||
|
||||
private fun submitProduct() {
|
||||
val name = binding.edtNamaProduk.text.toString()
|
||||
val description = binding.edtDeskripsiProduk.text.toString()
|
||||
val price = binding.edtHargaProduk.text.toString().toIntOrNull() ?: 0
|
||||
val stock = binding.edtStokProduk.text.toString().toIntOrNull() ?: 0
|
||||
val minOrder = binding.edtMinOrder.text.toString().toIntOrNull() ?: 1
|
||||
val weight = binding.edtBeratProduk.text.toString().toIntOrNull() ?: 0
|
||||
val price = binding.edtHargaProduk.text.toString().toInt()
|
||||
val stock = binding.edtStokProduk.text.toString().toInt()
|
||||
val minOrder = binding.edtMinOrder.text.toString().toInt()
|
||||
val weight = binding.edtBeratProduk.text.toString().toInt()
|
||||
val isPreOrder = binding.switchIsPreOrder.isChecked
|
||||
val duration = binding.edtDurasi.text.toString().toIntOrNull() ?: 0
|
||||
val duration = if (isPreOrder) binding.edtDurasi.text.toString().toInt() else 0
|
||||
val isActive = binding.switchIsActive.isChecked
|
||||
val categoryPosition = binding.spinnerKategoriProduk.selectedItemPosition
|
||||
val categoryId = categoryList.getOrNull(categoryPosition)?.id ?: 0
|
||||
val categoryId = categoryList.getOrNull(binding.spinnerKategoriProduk.selectedItemPosition)?.id ?: 0
|
||||
|
||||
if (isPreOrder && duration == 0) {
|
||||
Toast.makeText(this, "Durasi wajib diisi jika pre-order diaktifkan.", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
val imageFile = imageUri?.let { uriToFile(it, this) }
|
||||
val sppirtFile = sppirtUri?.let { uriToFile(it, this) }
|
||||
val halalFile = halalUri?.let { uriToFile(it, this) }
|
||||
|
||||
viewModel.addProduct(
|
||||
name, description, price, stock, minOrder, weight, isPreOrder, duration, categoryId, isActive
|
||||
name, description, price, stock, minOrder, weight, isPreOrder, duration, categoryId, isActive, imageFile, sppirtFile, halalFile
|
||||
).observe(this) { result ->
|
||||
when (result) {
|
||||
is Result.Loading -> binding.btnSaveProduct.isEnabled = false
|
||||
@ -123,53 +224,15 @@ class StoreProductDetailActivity : AppCompatActivity() {
|
||||
finish()
|
||||
}
|
||||
is Result.Error -> {
|
||||
binding.btnSaveProduct.isEnabled = true
|
||||
Toast.makeText(this, "Gagal: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
||||
binding.btnSaveProduct.isEnabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupFormValidation() {
|
||||
val watcher = object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
validateForm()
|
||||
}
|
||||
}
|
||||
|
||||
// Watch all fields
|
||||
binding.edtNamaProduk.addTextChangedListener(watcher)
|
||||
binding.edtDeskripsiProduk.addTextChangedListener(watcher)
|
||||
binding.edtHargaProduk.addTextChangedListener(watcher)
|
||||
binding.edtStokProduk.addTextChangedListener(watcher)
|
||||
binding.edtMinOrder.addTextChangedListener(watcher)
|
||||
binding.edtBeratProduk.addTextChangedListener(watcher)
|
||||
binding.edtDurasi.addTextChangedListener(watcher)
|
||||
}
|
||||
|
||||
private fun validateForm() {
|
||||
val isNameValid = binding.edtNamaProduk.text.toString().isNotBlank()
|
||||
val isDescriptionValid = binding.edtDeskripsiProduk.text.toString().isNotBlank()
|
||||
val isPriceValid = binding.edtHargaProduk.text.toString().isNotBlank()
|
||||
val isStockValid = binding.edtStokProduk.text.toString().isNotBlank()
|
||||
val isMinOrderValid = binding.edtMinOrder.text.toString().isNotBlank()
|
||||
val isWeightValid = binding.edtBeratProduk.text.toString().isNotBlank()
|
||||
val isPreOrderChecked = binding.switchIsPreOrder.isChecked
|
||||
val isDurationValid = !isPreOrderChecked || binding.edtDurasi.text.toString().isNotBlank()
|
||||
|
||||
val isFormValid = isNameValid && isDescriptionValid && isPriceValid &&
|
||||
isStockValid && isMinOrderValid && isWeightValid && isDurationValid
|
||||
|
||||
if (isFormValid) {
|
||||
binding.btnSaveProduct.isEnabled = true
|
||||
binding.btnSaveProduct.setBackgroundResource(R.drawable.bg_button_active)
|
||||
binding.btnSaveProduct.setTextColor(ContextCompat.getColor(this, R.color.white))
|
||||
} else {
|
||||
binding.btnSaveProduct.isEnabled = false
|
||||
binding.btnSaveProduct.setBackgroundResource(R.drawable.bg_button_disabled)
|
||||
binding.btnSaveProduct.setTextColor(ContextCompat.getColor(this, R.color.black_300))
|
||||
}
|
||||
private fun setupHeader() {
|
||||
binding.header.headerTitle.text = "Tambah Produk"
|
||||
binding.header.headerLeftIcon.setOnClickListener { onBackPressedDispatcher.onBackPressed() }
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@ 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
|
||||
import java.io.File
|
||||
|
||||
class ProductViewModel(private val repository: ProductRepository) : ViewModel() {
|
||||
|
||||
@ -74,17 +75,15 @@ class ProductViewModel(private val repository: ProductRepository) : ViewModel()
|
||||
isPreOrder: Boolean,
|
||||
duration: Int,
|
||||
categoryId: Int,
|
||||
isActive: Boolean
|
||||
isActive: Boolean,
|
||||
image: File?,
|
||||
sppirt: File?,
|
||||
halal: File?
|
||||
): LiveData<Result<Unit>> = liveData {
|
||||
emit(Result.Loading)
|
||||
val result = repository.addProduct(
|
||||
name, description, price, stock, minOrder, weight, isPreOrder, duration, categoryId, isActive
|
||||
)
|
||||
emit(result)
|
||||
emit(repository.addProduct(name, description, price, stock, minOrder, weight, isPreOrder, duration, categoryId, isActive, image, sppirt, halal))
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Optional: for store detail if you need it later
|
||||
// fun loadStoreDetail(storeId: Int) {
|
||||
// viewModelScope.launch {
|
||||
@ -92,4 +91,5 @@ class ProductViewModel(private val repository: ProductRepository) : ViewModel()
|
||||
// _storeDetail.value = storeResult
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
10
app/src/main/res/drawable/bg_upload.xml
Normal file
10
app/src/main/res/drawable/bg_upload.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/black_100"
|
||||
android:dashWidth="5dp"
|
||||
android:dashGap="5dp" />
|
||||
<solid android:color="@color/white" />
|
||||
</shape>
|
BIN
app/src/main/res/drawable/ic_close.png
Normal file
BIN
app/src/main/res/drawable/ic_close.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 521 B |
@ -69,15 +69,61 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- ImageView untuk preview gambar -->
|
||||
<ImageView
|
||||
android:id="@+id/iv_preview_foto"
|
||||
android:layout_width="72dp"
|
||||
android:layout_height="72dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:contentDescription="Preview Gambar"
|
||||
android:src="@drawable/ic_upload"/>
|
||||
<!-- ViewSwitcher untuk Upload Gambar Produk -->
|
||||
<ViewSwitcher
|
||||
android:id="@+id/switcher_foto_produk"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="24dp">
|
||||
|
||||
<!-- Tampilan saat belum ada gambar di-upload -->
|
||||
<FrameLayout
|
||||
android:id="@+id/layout_upload_foto"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_upload"
|
||||
android:gravity="center"
|
||||
android:padding="13dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="44dp"
|
||||
android:layout_height="44dp"
|
||||
android:src="@drawable/ic_upload"
|
||||
android:contentDescription="Ikon Upload" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<!-- Tampilan saat gambar sudah di-upload -->
|
||||
<FrameLayout
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:background="@drawable/bg_upload"
|
||||
android:layout_gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_preview_foto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop"
|
||||
android:contentDescription="Preview Gambar"
|
||||
android:src="@drawable/placeholder_image"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btn_remove_foto"
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="12dp"
|
||||
android:layout_gravity="top|end"
|
||||
android:layout_margin="6dp"
|
||||
android:elevation="8dp"
|
||||
android:src="@drawable/ic_close"
|
||||
android:contentDescription="Hapus Gambar"
|
||||
android:padding="4dp"
|
||||
app:tint="@color/white" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</ViewSwitcher>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@ -245,7 +291,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Harga Produk"
|
||||
style="@style/body_medium"
|
||||
android:layout_marginRight="4dp"/>
|
||||
android:layout_marginEnd="4dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
@ -409,7 +455,8 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:background="@drawable/bg_text_field"
|
||||
android:gravity="center">
|
||||
android:gravity="center"
|
||||
android:layout_marginTop="10dp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/edt_berat_produk"
|
||||
@ -549,7 +596,6 @@
|
||||
style="@style/body_medium"
|
||||
android:layout_marginEnd="4dp"/>
|
||||
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
@ -567,7 +613,8 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:background="@drawable/bg_text_field"
|
||||
android:gravity="center">
|
||||
android:gravity="center"
|
||||
android:layout_marginTop="10dp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/edt_durasi"
|
||||
@ -592,6 +639,178 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- SPPIRT -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="24dp">
|
||||
|
||||
<!-- Label SPPIRT -->
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="SPPIRT"
|
||||
style="@style/body_medium"/>
|
||||
|
||||
<!-- ViewSwitcher: Switches antara upload box dan uploaded preview SPPIRT -->
|
||||
<ViewSwitcher
|
||||
android:id="@+id/switcher_sppirt"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp">
|
||||
|
||||
<!-- Upload Prompt Layout -->
|
||||
<FrameLayout
|
||||
android:id="@+id/layout_upload_sppirt"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_upload"
|
||||
android:padding="20dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:src="@drawable/ic_upload"
|
||||
android:contentDescription="Ikon Unggah" />
|
||||
|
||||
<TextView
|
||||
style="@style/body_small"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Unggah dokumen SPPIRT Anda di sini"
|
||||
android:textColor="@color/black_300"
|
||||
android:layout_marginTop="8dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<!-- Uploaded File Preview Layout -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:background="@drawable/bg_text_field"
|
||||
android:gravity="center"
|
||||
android:layout_marginTop="10dp"
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_sppirt_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="sppirt.pdf"
|
||||
style="@style/body_small"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btn_remove_sppirt"
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="12dp"
|
||||
android:src="@drawable/ic_close"
|
||||
app:tint="@color/black_300"
|
||||
android:contentDescription="Hapus unggahan" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ViewSwitcher>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Halal -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="24dp">
|
||||
|
||||
<!-- Label Halal -->
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Sertifikasi Halal"
|
||||
style="@style/body_medium"/>
|
||||
|
||||
<!-- ViewSwitcher: Switches antara upload box dan uploaded preview halal -->
|
||||
<ViewSwitcher
|
||||
android:id="@+id/switcher_halal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp">
|
||||
|
||||
<!-- Upload Prompt Layout -->
|
||||
<FrameLayout
|
||||
android:id="@+id/layout_upload_halal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_upload"
|
||||
android:padding="20dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:src="@drawable/ic_upload"
|
||||
android:contentDescription="Ikon Unggah" />
|
||||
|
||||
<TextView
|
||||
style="@style/body_small"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Unggah dokumen Sertifikasi Halal Anda di sini"
|
||||
android:textColor="@color/black_300"
|
||||
android:layout_marginTop="8dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<!-- Uploaded File Preview Layout -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:background="@drawable/bg_text_field"
|
||||
android:gravity="center"
|
||||
android:layout_marginTop="10dp"
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_halal_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="sppirt.pdf"
|
||||
style="@style/body_small"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btn_remove_halal"
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="12dp"
|
||||
android:src="@drawable/ic_close"
|
||||
app:tint="@color/black_300"
|
||||
android:contentDescription="Hapus unggahan" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ViewSwitcher>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Produk Aktif -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
Reference in New Issue
Block a user