From 6a887a22aa7f564e85b5e38fcdf943f7fa3f0bfd Mon Sep 17 00:00:00 2001
From: Gracia Hotmauli <95269134+hotmauligracia@users.noreply.github.com>
Date: Mon, 26 May 2025 04:41:47 +0700
Subject: [PATCH] edit store profile
---
app/src/main/AndroidManifest.xml | 6 -
.../data/api/retrofit/ApiService.kt | 1 +
.../data/repository/MyStoreRepository.kt | 51 ++-
.../ui/profile/mystore/MyStoreActivity.kt | 4 +-
.../profile/DetailStoreProfileActivity.kt | 290 +++++++++++++----
.../profile/EditStoreProfileActivity.kt | 304 ------------------
.../utils/viewmodel/MyStoreViewModel.kt | 68 +++-
.../layout/activity_detail_store_profile.xml | 34 +-
app/src/main/res/layout/activity_my_store.xml | 6 +-
.../main/res/layout/dialog_store_image.xml | 17 +
10 files changed, 392 insertions(+), 389 deletions(-)
delete mode 100644 app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/EditStoreProfileActivity.kt
create mode 100644 app/src/main/res/layout/dialog_store_image.xml
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c7690ea..31e1666 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -47,18 +47,12 @@
-
-
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 c889c3c..57e4742 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
@@ -382,6 +382,7 @@ interface ApiService {
@Part("latitude") latitude: RequestBody,
@Part("longitude") longitude: RequestBody,
@Part("user_phone") userPhone: RequestBody,
+ @Part("store_type_id") storeTypeId: RequestBody,
@Part storeimg: MultipartBody.Part?
): Response
diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/repository/MyStoreRepository.kt b/app/src/main/java/com/alya/ecommerce_serang/data/repository/MyStoreRepository.kt
index c14a0fd..801c8ea 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/data/repository/MyStoreRepository.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/data/repository/MyStoreRepository.kt
@@ -2,9 +2,14 @@ package com.alya.ecommerce_serang.data.repository
import android.util.Log
import com.alya.ecommerce_serang.data.api.dto.Store
+import com.alya.ecommerce_serang.data.api.response.auth.ListStoreTypeResponse
import com.alya.ecommerce_serang.data.api.response.customer.product.StoreResponse
+import com.alya.ecommerce_serang.data.api.response.store.profile.StoreDataResponse
import com.alya.ecommerce_serang.data.api.retrofit.ApiService
+import okhttp3.MultipartBody
+import okhttp3.RequestBody
import retrofit2.HttpException
+import retrofit2.Response
import java.io.IOException
class MyStoreRepository(private val apiService: ApiService) {
@@ -14,18 +19,56 @@ class MyStoreRepository(private val apiService: ApiService) {
if (response.isSuccessful) {
val storeResponse: StoreResponse? = response.body()
- Result.Success(storeResponse?.store) // ✅ Return Success with Store data
+ Result.Success(storeResponse?.store)
} else {
val errorMessage = response.errorBody()?.string() ?: "Unknown API error"
Log.e("MyStoreRepository", "Error: $errorMessage")
- Result.Error(HttpException(response)) // ✅ Wrap API error in Result.Error
+ Result.Error(HttpException(response))
}
} catch (e: IOException) {
Log.e("MyStoreRepository", "Network error: ${e.message}")
- Result.Error(e) // ✅ Handle network-related errors
+ Result.Error(e)
} catch (e: Exception) {
Log.e("MyStoreRepository", "Unexpected error: ${e.message}")
- Result.Error(e) // ✅ Handle unexpected errors
+ Result.Error(e)
}
}
+
+ suspend fun listStoreType(): Result{
+ return try{
+ val response = apiService.listTypeStore()
+ if (response.isSuccessful) {
+ response.body()?.let {
+ Result.Success(it)
+ } ?: Result.Error(Exception("No store type"))
+ } else {
+ throw Exception("No response ${response.errorBody()?.string()}")
+ }
+ } catch (e:Exception){
+ Result.Error(e)
+ }
+ }
+
+ suspend fun updateStoreProfile(
+ storeName: RequestBody,
+ storeStatus: RequestBody,
+ storeDescription: RequestBody,
+ isOnLeave: RequestBody,
+ cityId: RequestBody,
+ provinceId: RequestBody,
+ street: RequestBody,
+ subdistrict: RequestBody,
+ detail: RequestBody,
+ postalCode: RequestBody,
+ latitude: RequestBody,
+ longitude: RequestBody,
+ userPhone: RequestBody,
+ storeType: RequestBody,
+ storeimg: MultipartBody.Part?
+ ): Response {
+ return apiService.updateStoreProfileMultipart(
+ storeName, storeStatus, storeDescription, isOnLeave, cityId, provinceId,
+ street, subdistrict, detail, postalCode, latitude, longitude, userPhone, storeType, storeimg
+ )
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/MyStoreActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/MyStoreActivity.kt
index a40035b..5911667 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/MyStoreActivity.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/MyStoreActivity.kt
@@ -66,7 +66,7 @@ class MyStoreActivity : AppCompatActivity() {
binding.tvStoreName.text = store.storeName
binding.tvStoreType.text = store.storeType
- if (store.storeImage != null && store.storeImage.toString().isNotEmpty() && store.storeImage.toString() != "null") {
+ if (store.storeImage.toString().isNotEmpty() && store.storeImage.toString() != "null") {
val imageUrl = "$BASE_URL${store.storeImage}"
Log.d("MyStoreActivity", "Loading store image from: $imageUrl")
@@ -78,6 +78,8 @@ class MyStoreActivity : AppCompatActivity() {
} else {
Log.d("MyStoreActivity", "No store image available")
}
+
+// binding.tvBalance.text = String.format("Rp%,.0f", store.balance.toString())
}
private fun setUpClickListeners() {
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/DetailStoreProfileActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/DetailStoreProfileActivity.kt
index 44a375a..2f06c74 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/DetailStoreProfileActivity.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/DetailStoreProfileActivity.kt
@@ -1,20 +1,30 @@
package com.alya.ecommerce_serang.ui.profile.mystore.profile
import android.app.Activity
+import android.app.AlertDialog
import android.content.Intent
+import android.net.Uri
import android.os.Bundle
-import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
+import android.widget.TextView
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
+import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import com.alya.ecommerce_serang.BuildConfig.BASE_URL
import com.alya.ecommerce_serang.R
import com.alya.ecommerce_serang.data.api.dto.Store
+import com.alya.ecommerce_serang.data.api.response.auth.StoreTypesItem
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.MyStoreRepository
import com.alya.ecommerce_serang.databinding.ActivityDetailStoreProfileBinding
+import com.alya.ecommerce_serang.databinding.DialogStoreImageBinding
import com.alya.ecommerce_serang.ui.profile.mystore.profile.address.DetailStoreAddressActivity
import com.alya.ecommerce_serang.ui.profile.mystore.profile.payment_info.PaymentInfoActivity
import com.alya.ecommerce_serang.ui.profile.mystore.profile.shipping_service.ShippingServiceActivity
@@ -22,12 +32,26 @@ import com.alya.ecommerce_serang.utils.viewmodel.MyStoreViewModel
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
import com.alya.ecommerce_serang.utils.SessionManager
import com.bumptech.glide.Glide
+import okhttp3.MediaType.Companion.toMediaTypeOrNull
+import okhttp3.MultipartBody
+import okhttp3.RequestBody
+import okhttp3.RequestBody.Companion.asRequestBody
+import java.io.File
+import java.io.FileOutputStream
import kotlin.getValue
class DetailStoreProfileActivity : AppCompatActivity() {
private lateinit var binding: ActivityDetailStoreProfileBinding
private lateinit var apiService: ApiService
private lateinit var sessionManager: SessionManager
+ private var currentStore: Store? = null
+ private var editMode = false
+ private var selectedImageUri: Uri? = null
+ private var selectedStoreTypeId: Int = -1
+
+ private var storeTypesLoaded: Boolean = false
+ private var currentStoreLoaded: Boolean = false
+ private var storeTypesList: List = listOf()
private val viewModel: MyStoreViewModel by viewModels {
BaseViewModelFactory {
@@ -37,6 +61,13 @@ class DetailStoreProfileActivity : AppCompatActivity() {
}
}
+ private val imagePickerLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
+ uri?.let {
+ selectedImageUri = it
+ Glide.with(this).load(it).into(binding.ivProfile)
+ }
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityDetailStoreProfileBinding.inflate(layoutInflater)
@@ -55,97 +86,232 @@ class DetailStoreProfileActivity : AppCompatActivity() {
onBackPressedDispatcher.onBackPressed()
}
+ viewModel.loadMyStore()
+ viewModel.fetchStoreTypes()
+
+ viewModel.myStoreProfile.observe(this) {
+ currentStore = it
+ currentStoreLoaded = true
+ if (storeTypesLoaded) setupStoreTypeSpinner(storeTypesList)
+ updateUI(it)
+ }
+
+ viewModel.storeTypes.observe(this) {
+ storeTypesList = it
+ storeTypesLoaded = true
+ if (currentStoreLoaded) setupStoreTypeSpinner(storeTypesList)
+ }
+
+ binding.ivProfile.setOnClickListener {
+ if (editMode) showImageOptions()
+ }
+
binding.btnEditStoreProfile.setOnClickListener {
- val intent = Intent(this, EditStoreProfileActivity::class.java)
- startActivityForResult(intent, EDIT_PROFILE_REQUEST_CODE)
+ if (editMode) {
+ if (hasChanges()) confirmUpdate() else exitEditMode()
+ } else {
+ enterEditMode()
+ }
+// val intent = Intent(this, EditStoreProfileActivity::class.java)
+// startActivityForResult(intent, EDIT_PROFILE_REQUEST_CODE)
}
binding.layoutAddress.setOnClickListener {
- val intent = Intent(this, DetailStoreAddressActivity::class.java)
- startActivityForResult(intent, ADDRESS_REQUEST_CODE)
+ startActivityForResult(Intent(this, DetailStoreAddressActivity::class.java), 101)
}
- // Set up payment method layout click listener
binding.layoutPaymentMethod.setOnClickListener {
- val intent = Intent(this, PaymentInfoActivity::class.java)
- startActivityForResult(intent, PAYMENT_INFO_REQUEST_CODE)
+ startActivityForResult(Intent(this, PaymentInfoActivity::class.java), 102)
}
- // Set up shipping services layout click listener
binding.layoutShipServices.setOnClickListener {
- val intent = Intent(this, ShippingServiceActivity::class.java)
- startActivityForResult(intent, SHIPPING_SERVICES_REQUEST_CODE)
+ startActivityForResult(Intent(this, ShippingServiceActivity::class.java), 103)
}
- viewModel.loadMyStore()
+ observeViewModel()
+ }
- viewModel.myStoreProfile.observe(this){ user ->
- user?.let { updateStoreProfile(it) }
+ private fun setupStoreTypeSpinner(storeTypes: List) {
+ val adapter = object : ArrayAdapter(
+ this,
+ android.R.layout.simple_spinner_item,
+ storeTypes
+ ) {
+ override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
+ val view = super.getView(position, convertView, parent)
+ (view as TextView).text = getItem(position)?.name ?: ""
+ return view
+ }
+
+ override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
+ val view = super.getDropDownView(position, convertView, parent)
+ (view as TextView).text = getItem(position)?.name ?: ""
+ return view
+ }
}
- viewModel.errorMessage.observe(this) { error ->
- Toast.makeText(this, error, Toast.LENGTH_SHORT).show()
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
+ binding.spinnerJenisToko.adapter = adapter
+
+ currentStore?.storeTypeId?.let { typeId ->
+ val index = storeTypes.indexOfFirst { it.id == typeId }
+ if (index >= 0) binding.spinnerJenisToko.setSelection(index)
+ }
+
+ binding.spinnerJenisToko.isEnabled = editMode
+ binding.spinnerJenisToko.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
+ override fun onItemSelected(parent: AdapterView<*>?, view: View?, pos: Int, id: Long) {
+ val selected = adapter.getItem(pos)
+ selected?.let {
+ selectedStoreTypeId = it.id
+ }
+ }
+
+ override fun onNothingSelected(parent: AdapterView<*>?) {}
}
}
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- super.onActivityResult(requestCode, resultCode, data)
- if (requestCode == EDIT_PROFILE_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
- // Refresh the profile data
+ private fun observeViewModel() {
+ viewModel.updateStoreProfileResult.observe(this) {
Toast.makeText(this, "Profil toko berhasil diperbarui", Toast.LENGTH_SHORT).show()
viewModel.loadMyStore()
+ exitEditMode()
+ }
- // Pass the result back to parent activity
- setResult(Activity.RESULT_OK)
- } else if (requestCode == ADDRESS_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
- // Refresh the profile data after address update
- Toast.makeText(this, "Alamat toko berhasil diperbarui", Toast.LENGTH_SHORT).show()
- viewModel.loadMyStore()
-
- // Pass the result back to parent activity
- setResult(Activity.RESULT_OK)
- } else if (requestCode == PAYMENT_INFO_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
- // Refresh the profile data after payment method update
- Toast.makeText(this, "Metode pembayaran berhasil diperbarui", Toast.LENGTH_SHORT).show()
- viewModel.loadMyStore()
-
- // Pass the result back to parent activity
- setResult(Activity.RESULT_OK)
- } else if (requestCode == SHIPPING_SERVICES_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
- // Refresh the profile data after shipping services update
- Toast.makeText(this, "Layanan pengiriman berhasil diperbarui", Toast.LENGTH_SHORT).show()
- viewModel.loadMyStore()
-
- // Pass the result back to parent activity
- setResult(Activity.RESULT_OK)
+ viewModel.errorMessage.observe(this) {
+ Toast.makeText(this, it, Toast.LENGTH_SHORT).show()
}
}
- companion object {
- private const val EDIT_PROFILE_REQUEST_CODE = 100
- private const val ADDRESS_REQUEST_CODE = 101
- private const val PAYMENT_INFO_REQUEST_CODE = 102
- private const val SHIPPING_SERVICES_REQUEST_CODE = 103
- }
-
- private fun updateStoreProfile(store: Store){
- // Update text fields
- binding.edtNamaToko.setText(store.storeName.toString())
- binding.edtJenisToko.setText(store.storeType.toString())
- binding.edtDeskripsiToko.setText(store.storeDescription.toString())
-
- // Update store image if available
- if (store.storeImage != null && store.storeImage.toString().isNotEmpty() && store.storeImage.toString() != "null") {
- val imageUrl = "$BASE_URL${store.storeImage}"
- Log.d("DetailStoreProfile", "Loading image from: $imageUrl")
+ private fun updateUI(store: Store?) {
+ store.let {
+ binding.edtNamaToko.setText(it?.storeName)
+ binding.edtDeskripsiToko.setText(it?.storeDescription)
+ binding.switchIsActive.isChecked = it?.isOnLeave == true
+ val imageUrl = when {
+ it?.storeImage.toString().isBlank() -> null
+ it?.storeImage.toString().startsWith("http") -> it?.storeImage
+ it?.storeImage.toString().startsWith("/") -> BASE_URL + it?.storeImage.toString().removePrefix("/")
+ else -> BASE_URL + it?.storeImage
+ }
Glide.with(this)
.load(imageUrl)
.placeholder(R.drawable.placeholder_image)
.error(R.drawable.placeholder_image)
.into(binding.ivProfile)
- } else {
- Log.d("DetailStoreProfile", "No store image available")
+
+ setFieldsEnabled(false)
}
}
+
+ private fun setFieldsEnabled(enabled: Boolean) {
+ binding.edtNamaToko.isEnabled = enabled
+ binding.edtNamaToko.setBackgroundResource(R.drawable.bg_text_field)
+
+ binding.spinnerJenisToko.isEnabled = enabled
+ binding.layoutJenisToko.setBackgroundResource(R.drawable.bg_text_field)
+
+ binding.edtDeskripsiToko.isEnabled = enabled
+ binding.edtDeskripsiToko.setBackgroundResource(R.drawable.bg_text_field)
+
+ binding.switchIsActive.isEnabled = enabled
+ }
+
+ private fun enterEditMode() {
+ editMode = true
+ setFieldsEnabled(true)
+ binding.btnEditStoreProfile.text = "Simpan Perubahan"
+ binding.btnEditStoreProfile.setBackgroundResource(R.drawable.bg_button_active)
+ binding.btnEditStoreProfile.setTextColor(getColor(R.color.white))
+ }
+
+ private fun exitEditMode() {
+ editMode = false
+ setFieldsEnabled(false)
+ binding.btnEditStoreProfile.text = "Ubah Profil"
+ binding.btnEditStoreProfile.setBackgroundResource(R.drawable.bg_button_secondary)
+ binding.btnEditStoreProfile.setTextColor(getColor(R.color.blue_500))
+ }
+
+ private fun hasChanges(): Boolean {
+ val nameChanged = binding.edtNamaToko.text.toString() != currentStore?.storeName
+ val descChanged = binding.edtDeskripsiToko.text.toString() != currentStore?.storeDescription
+ val isOnLeaveChanged = (binding.switchIsActive.isChecked && currentStore?.isOnLeave != false)
+ || (!binding.switchIsActive.isChecked && currentStore?.isOnLeave == false)
+ val imageChanged = selectedImageUri != null
+ val storeTypeChanged = selectedStoreTypeId != currentStore?.storeTypeId
+ return nameChanged || descChanged || isOnLeaveChanged || imageChanged || storeTypeChanged
+ }
+
+ private fun confirmUpdate() {
+ AlertDialog.Builder(this)
+ .setTitle("Konfirmasi Perubahan")
+ .setMessage("Apakah Anda yakin ingin menyimpan perubahan profil toko Anda?")
+ .setPositiveButton("Ya") { _, _ -> updateStoreProfile() }
+ .setNegativeButton("Batal", null)
+ .show()
+ }
+
+ private fun showImageOptions() {
+ val options = arrayOf("Lihat Foto", "Ganti Foto")
+ AlertDialog.Builder(this)
+ .setItems(options) { _, which ->
+ when (which) {
+ 0 -> showImagePreviewDialog()
+ 1 -> imagePickerLauncher.launch("image/*")
+ }
+ }
+ .show()
+ }
+
+ private fun showImagePreviewDialog() {
+ val dialogBinding = DialogStoreImageBinding.inflate(LayoutInflater.from(this))
+ val imageUrl = when {
+ selectedImageUri != null -> selectedImageUri.toString()
+ currentStore?.storeImage.toString().isBlank() -> null
+ currentStore?.storeImage.toString().startsWith("http") == true -> currentStore?.storeImage
+ currentStore?.storeImage.toString().startsWith("/") == true -> BASE_URL + currentStore?.storeImage!!.toString().removePrefix("/")
+ else -> BASE_URL + currentStore?.storeImage
+ }
+ Glide.with(this)
+ .load(imageUrl)
+ .placeholder(R.drawable.placeholder_image)
+ .error(R.drawable.placeholder_image)
+ .into(dialogBinding.ivPreviewStoreImg)
+
+ AlertDialog.Builder(this)
+ .setView(dialogBinding.root)
+ .setPositiveButton("Tutup", null)
+ .show()
+ }
+
+ private fun updateStoreProfile() {
+ val storeName = binding.edtNamaToko.text.toString().toRequestBody()
+ val storeType = selectedStoreTypeId.toString().toRequestBody()
+ val description = binding.edtDeskripsiToko.text.toString().toRequestBody()
+ val isOnLeave = binding.switchIsActive.isChecked.toString().toRequestBody()
+
+ val imagePart = selectedImageUri?.let {
+ val file = uriToNamedFile(it, this, "storeimg")
+ MultipartBody.Part.createFormData(
+ "storeimg", file.name, file.asRequestBody(
+ contentResolver.getType(it)?.toMediaTypeOrNull()
+ )
+ )
+ }
+
+ viewModel.updateStoreProfile(storeName, storeType, description, isOnLeave, imagePart)
+ }
+
+ private fun uriToNamedFile(uri: Uri, context: Activity, prefix: String): File {
+ val extension = contentResolver.getType(uri)?.substringAfter("/") ?: "jpg"
+ val filename = "$prefix-${System.currentTimeMillis()}.$extension"
+ val file = File(context.cacheDir, filename)
+ contentResolver.openInputStream(uri)?.use { input -> FileOutputStream(file).use { output -> input.copyTo(output) } }
+ return file
+ }
+
+ private fun String.toRequestBody(): RequestBody =
+ RequestBody.create("text/plain".toMediaTypeOrNull(), this)
}
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/EditStoreProfileActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/EditStoreProfileActivity.kt
deleted file mode 100644
index 2d31f3b..0000000
--- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/profile/EditStoreProfileActivity.kt
+++ /dev/null
@@ -1,304 +0,0 @@
-package com.alya.ecommerce_serang.ui.profile.mystore.profile
-
-import android.app.Activity
-import android.content.Intent
-import android.net.Uri
-import android.os.Bundle
-import android.provider.MediaStore
-import android.util.Log
-import android.view.View
-import android.widget.Toast
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.appcompat.app.AppCompatActivity
-import androidx.lifecycle.lifecycleScope
-import com.alya.ecommerce_serang.R
-import com.alya.ecommerce_serang.data.api.dto.Store
-import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
-import com.alya.ecommerce_serang.databinding.ActivityEditStoreProfileBinding
-import com.alya.ecommerce_serang.utils.SessionManager
-import com.bumptech.glide.Glide
-import kotlinx.coroutines.launch
-import okhttp3.MediaType.Companion.toMediaTypeOrNull
-import okhttp3.MultipartBody
-import okhttp3.RequestBody.Companion.asRequestBody
-import okhttp3.RequestBody.Companion.toRequestBody
-import java.io.File
-import java.io.FileOutputStream
-
-class EditStoreProfileActivity : AppCompatActivity() {
- private lateinit var binding: ActivityEditStoreProfileBinding
- private lateinit var sessionManager: SessionManager
- private var storeImageUri: Uri? = null
- private lateinit var currentStore: Store
-
- private val pickImage = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
- if (result.resultCode == Activity.RESULT_OK) {
- result.data?.data?.let { uri ->
- storeImageUri = uri
- Log.d("EditStoreProfile", "Image selected: $uri")
-
- // Set the image to the ImageView for immediate preview
- try {
- binding.ivStoreImage.setImageURI(null) // Clear any previous image
- binding.ivStoreImage.setImageURI(uri)
-
- // Alternative way using Glide for more reliable preview
- Glide.with(this)
- .load(uri)
- .placeholder(R.drawable.placeholder_image)
- .error(R.drawable.placeholder_image)
- .into(binding.ivStoreImage)
-
- Toast.makeText(this, "Gambar berhasil dipilih", Toast.LENGTH_SHORT).show()
- } catch (e: Exception) {
- Log.e("EditStoreProfile", "Error displaying image preview", e)
- Toast.makeText(this, "Gagal menampilkan preview gambar", Toast.LENGTH_SHORT).show()
- }
- }
- }
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- binding = ActivityEditStoreProfileBinding.inflate(layoutInflater)
- setContentView(binding.root)
-
- sessionManager = SessionManager(this)
-
- // Set up header
- binding.header.headerTitle.text = "Edit Profil Toko"
- binding.header.headerLeftIcon.setOnClickListener { finish() }
-
- loadStoreData()
- setupClickListeners()
- }
-
- private fun loadStoreData() {
- binding.progressBar.visibility = View.VISIBLE
- lifecycleScope.launch {
- try {
- val response = ApiConfig.getApiService(sessionManager).getStore()
- binding.progressBar.visibility = View.GONE
-
- if (response.isSuccessful && response.body() != null) {
- currentStore = response.body()!!.store
- populateFields(currentStore)
- } else {
- showError("Gagal memuat profil toko")
- }
- } catch (e: Exception) {
- binding.progressBar.visibility = View.GONE
- showError("Terjadi kesalahan: ${e.message}")
- }
- }
- }
-
- private fun populateFields(store: Store) {
- // Load store image
- if (store.storeImage != null && store.storeImage.toString().isNotEmpty() && store.storeImage.toString() != "null") {
- Glide.with(this)
- .load(store.storeImage.toString())
- .placeholder(R.drawable.placeholder_image)
- .error(R.drawable.placeholder_image)
- .into(binding.ivStoreImage)
- }
-
- // Set other fields
- binding.edtStoreName.setText(store.storeName)
- binding.edtDescription.setText(store.storeDescription)
- binding.edtUserPhone.setText(store.userPhone)
-
- // Set is on leave
- binding.switchIsOnLeave.isChecked = store.isOnLeave
- }
-
- private fun setupClickListeners() {
- binding.btnSelectStoreImage.setOnClickListener {
- val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
- pickImage.launch(intent)
- }
-
- binding.btnSave.setOnClickListener {
- saveStoreProfile()
- }
- }
-
- private fun saveStoreProfile() {
- val storeName = binding.edtStoreName.text.toString()
- val storeDescription = binding.edtDescription.text.toString()
- val userPhone = binding.edtUserPhone.text.toString()
- val storeStatus = currentStore.storeStatus // Keep the current status
- val isOnLeave = binding.switchIsOnLeave.isChecked
-
- if (storeName.isEmpty() || userPhone.isEmpty()) {
- showError("Nama toko dan nomor telepon harus diisi")
- return
- }
-
- binding.progressBar.visibility = View.VISIBLE
- binding.btnSave.isEnabled = false
-
- // Show progress indicator on the image if we're uploading one
- if (storeImageUri != null) {
- binding.progressImage.visibility = View.VISIBLE
- }
-
- lifecycleScope.launch {
- try {
- Log.d("EditStoreProfile", "Starting profile update process")
-
- // Create multipart request for image if selected
- var storeImagePart: MultipartBody.Part? = null
- if (storeImageUri != null) {
- try {
- val storeImageFile = uriToFile(storeImageUri!!)
- Log.d("EditStoreProfile", "Image file created: ${storeImageFile.name}, size: ${storeImageFile.length()}")
-
- // Get the MIME type
- val mimeType = contentResolver.getType(storeImageUri!!) ?: "image/jpeg"
- Log.d("EditStoreProfile", "MIME type: $mimeType")
-
- val storeImageRequestBody = storeImageFile.asRequestBody(mimeType.toMediaTypeOrNull())
- storeImagePart = MultipartBody.Part.createFormData("storeimg", storeImageFile.name, storeImageRequestBody)
- Log.d("EditStoreProfile", "Image part created with name: storeimg, filename: ${storeImageFile.name}")
- } catch (e: Exception) {
- Log.e("EditStoreProfile", "Error creating image part", e)
- runOnUiThread {
- Toast.makeText(this@EditStoreProfileActivity, "Error preparing image: ${e.message}", Toast.LENGTH_SHORT).show()
- }
- }
- }
-
- // Create text parts
- val nameRequestBody = storeName.toRequestBody("text/plain".toMediaTypeOrNull())
- val descriptionRequestBody = storeDescription.toRequestBody("text/plain".toMediaTypeOrNull())
- val userPhoneRequestBody = userPhone.toRequestBody("text/plain".toMediaTypeOrNull())
- val statusRequestBody = storeStatus.toRequestBody("text/plain".toMediaTypeOrNull())
- val onLeaveRequestBody = isOnLeave.toString().toRequestBody("text/plain".toMediaTypeOrNull())
-
- // Log request parameters
- Log.d("EditStoreProfile", "Request parameters: " +
- "\nstore_name: $storeName" +
- "\nstore_status: $storeStatus" +
- "\nstore_description: $storeDescription" +
- "\nis_on_leave: $isOnLeave" +
- "\nuser_phone: $userPhone" +
- "\nimage: ${storeImageUri != null}")
-
- // Log all parts for debugging
- Log.d("EditStoreProfile", "Request parts:" +
- "\nstoreName: $nameRequestBody" +
- "\nstoreStatus: $statusRequestBody" +
- "\nstoreDescription: $descriptionRequestBody" +
- "\nisOnLeave: $onLeaveRequestBody" +
- "\nuserPhone: $userPhoneRequestBody" +
- "\nstoreimg: ${storeImagePart != null}")
-
- val response = ApiConfig.getApiService(sessionManager).updateStoreProfileMultipart(
- storeName = nameRequestBody,
- storeStatus = statusRequestBody,
- storeDescription = descriptionRequestBody,
- isOnLeave = onLeaveRequestBody,
- cityId = currentStore.cityId.toString().toRequestBody("text/plain".toMediaTypeOrNull()),
- provinceId = currentStore.provinceId.toString().toRequestBody("text/plain".toMediaTypeOrNull()),
- street = currentStore.street.toRequestBody("text/plain".toMediaTypeOrNull()),
- subdistrict = currentStore.subdistrict.toRequestBody("text/plain".toMediaTypeOrNull()),
- detail = currentStore.detail.toRequestBody("text/plain".toMediaTypeOrNull()),
- postalCode = currentStore.postalCode.toRequestBody("text/plain".toMediaTypeOrNull()),
- latitude = currentStore.latitude.toRequestBody("text/plain".toMediaTypeOrNull()),
- longitude = currentStore.longitude.toRequestBody("text/plain".toMediaTypeOrNull()),
- userPhone = userPhoneRequestBody,
- storeimg = storeImagePart
- )
-
- Log.d("EditStoreProfile", "Response received: isSuccessful=${response.isSuccessful}, code=${response.code()}")
-
- runOnUiThread {
- binding.progressBar.visibility = View.GONE
- binding.progressImage.visibility = View.GONE
- binding.btnSave.isEnabled = true
-
- if (response.isSuccessful) {
- Log.d("EditStoreProfile", "Response body: ${response.body()?.toString()}")
- // Try to log the updated store image URL
- response.body()?.let { responseBody ->
- val updatedStoreImage = responseBody.store?.storeImage
- Log.d("EditStoreProfile", "Updated store image URL: $updatedStoreImage")
- }
- showSuccess("Profil toko berhasil diperbarui")
- setResult(Activity.RESULT_OK)
- finish()
- } else {
- val errorBodyString = response.errorBody()?.string() ?: "Error body is null"
- Log.e("EditStoreProfile", "Full error response: $errorBodyString")
- Log.e("EditStoreProfile", "Response headers: ${response.headers()}")
- showError("Gagal memperbarui profil toko (${response.code()})")
- }
- }
- } catch (e: Exception) {
- Log.e("EditStoreProfile", "Exception during API call", e)
-
- runOnUiThread {
- binding.progressBar.visibility = View.GONE
- binding.progressImage.visibility = View.GONE
- binding.btnSave.isEnabled = true
- showError("Error: ${e.message}")
- }
- }
- }
- }
-
- private fun uriToFile(uri: Uri): File {
- val contentResolver = applicationContext.contentResolver
- val fileExtension = getFileExtension(contentResolver, uri)
- val timeStamp = System.currentTimeMillis()
- val fileName = "IMG_${timeStamp}.$fileExtension"
- val tempFile = File(cacheDir, fileName)
-
- Log.d("EditStoreProfile", "Creating temp file: ${tempFile.absolutePath}")
-
- try {
- contentResolver.openInputStream(uri)?.use { inputStream ->
- FileOutputStream(tempFile).use { outputStream ->
- val buffer = ByteArray(4 * 1024) // 4k buffer
- var bytesRead: Int
- while (inputStream.read(buffer).also { bytesRead = it } != -1) {
- outputStream.write(buffer, 0, bytesRead)
- }
- outputStream.flush()
- }
- }
-
- Log.d("EditStoreProfile", "File created successfully: ${tempFile.name}, size: ${tempFile.length()} bytes")
- return tempFile
- } catch (e: Exception) {
- Log.e("EditStoreProfile", "Error creating file from URI", e)
- throw e
- }
- }
-
- private fun getFileExtension(contentResolver: android.content.ContentResolver, uri: Uri): String {
- val mimeType = contentResolver.getType(uri)
- return if (mimeType != null) {
- val mime = android.webkit.MimeTypeMap.getSingleton()
- mime.getExtensionFromMimeType(mimeType) ?: "jpg"
- } else {
- // If mime type is null, try to get from URI path
- val path = uri.path
- if (path != null) {
- val extension = android.webkit.MimeTypeMap.getFileExtensionFromUrl(path)
- if (!extension.isNullOrEmpty()) {
- extension
- } else "jpg"
- } else "jpg"
- }
- }
-
- private fun showSuccess(message: String) {
- Toast.makeText(this, message, Toast.LENGTH_LONG).show()
- }
-
- private fun showError(message: String) {
- Toast.makeText(this, message, Toast.LENGTH_LONG).show()
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/MyStoreViewModel.kt b/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/MyStoreViewModel.kt
index 9ca5b34..5dcb052 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/MyStoreViewModel.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/MyStoreViewModel.kt
@@ -5,24 +5,88 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.alya.ecommerce_serang.data.api.dto.Store
+import com.alya.ecommerce_serang.data.api.response.auth.StoreTypesItem
+import com.alya.ecommerce_serang.data.api.response.store.profile.StoreDataResponse
import com.alya.ecommerce_serang.data.repository.MyStoreRepository
import com.alya.ecommerce_serang.data.repository.Result
import kotlinx.coroutines.launch
+import okhttp3.MediaType.Companion.toMediaTypeOrNull
+import okhttp3.MultipartBody
+import okhttp3.RequestBody
-class MyStoreViewModel(private val myStoreRepository: MyStoreRepository): ViewModel() {
+class MyStoreViewModel(private val repository: MyStoreRepository): ViewModel() {
private val _myStoreProfile = MutableLiveData()
val myStoreProfile: LiveData = _myStoreProfile
+ private val _storeTypes = MutableLiveData>()
+ val storeTypes: LiveData> = _storeTypes
+
+ private val _isLoadingType = MutableLiveData()
+ val isLoadingType: LiveData = _isLoadingType
+
+ private val _updateStoreProfileResult = MutableLiveData()
+ val updateStoreProfileResult: LiveData = _updateStoreProfileResult
+
private val _errorMessage = MutableLiveData()
val errorMessage : LiveData = _errorMessage
fun loadMyStore(){
viewModelScope.launch {
- when (val result = myStoreRepository.fetchMyStoreProfile()){
+ when (val result = repository.fetchMyStoreProfile()){
is Result.Success -> _myStoreProfile.postValue(result.data)
is Result.Error -> _errorMessage.postValue(result.exception.message ?: "Unknown Error")
is Result.Loading -> null
}
}
}
+
+ fun fetchStoreTypes() {
+ _isLoadingType.value = true
+ viewModelScope.launch {
+ when (val result = repository.listStoreType()) {
+ is Result.Success -> {
+ _storeTypes.value = result.data.storeTypes
+ _isLoadingType.value = false
+ }
+ is Result.Error -> {
+ _errorMessage.value = result.exception.message ?: "Unknown error occurred"
+ _isLoadingType.value = false
+ }
+ is Result.Loading -> {
+ _isLoadingType.value = true
+ }
+ }
+ }
+ }
+
+ fun updateStoreProfile(
+ storeName: RequestBody,
+ storeType: RequestBody,
+ description: RequestBody,
+ isOnLeave: RequestBody,
+ storeImage: MultipartBody.Part?
+ ) {
+ viewModelScope.launch {
+ try {
+ val response = repository.updateStoreProfile(
+ storeName,
+ "active".toRequestBody(),
+ description,
+ isOnLeave,
+ 0.toString().toRequestBody(),
+ 0.toString().toRequestBody(),
+ "".toRequestBody(), "".toRequestBody(), "".toRequestBody(), "".toRequestBody(),
+ "".toRequestBody(), "".toRequestBody(), "".toRequestBody(),
+ storeType, storeImage
+ )
+ if (response.isSuccessful) _updateStoreProfileResult.postValue(response.body())
+ else _errorMessage.postValue("Gagal memperbarui profil")
+ } catch (e: Exception) {
+ _errorMessage.postValue(e.message ?: "Unexpected error")
+ }
+ }
+ }
+
+ private fun String.toRequestBody(): RequestBody =
+ RequestBody.create("text/plain".toMediaTypeOrNull(), this)
}
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_detail_store_profile.xml b/app/src/main/res/layout/activity_detail_store_profile.xml
index a32534f..d88cebf 100644
--- a/app/src/main/res/layout/activity_detail_store_profile.xml
+++ b/app/src/main/res/layout/activity_detail_store_profile.xml
@@ -2,6 +2,7 @@
-
+
+ android:gravity="center_vertical"
+ android:layout_marginTop="10dp">
+
+
+
+
+
+
+
@@ -160,7 +180,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:text="Toko Aktif"
+ android:text="Toko Libur"
style="@style/label_large"/>
+ android:text="Pesanan Masuk"/>
diff --git a/app/src/main/res/layout/dialog_store_image.xml b/app/src/main/res/layout/dialog_store_image.xml
new file mode 100644
index 0000000..ba9a4e7
--- /dev/null
+++ b/app/src/main/res/layout/dialog_store_image.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
\ No newline at end of file