diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f7e9e77..54fdf3b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -29,6 +29,12 @@ android:theme="@style/Theme.Ecommerce_serang" android:usesCleartextTraffic="true" tools:targetApi="31"> + + @@ -48,7 +54,7 @@ android:name=".ui.product.storeDetail.StoreDetailActivity" android:exported="false" /> ? = null, + + @field:SerializedName("message") + val message: String? = null +) diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/sells/OrderListResponse.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/sells/OrderListResponse.kt index 5439b14..5efc65d 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/sells/OrderListResponse.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/response/store/sells/OrderListResponse.kt @@ -129,5 +129,7 @@ data class OrdersItem( val status: String? = null, @field:SerializedName("city_id") - val cityId: Int? = null + val cityId: Int? = null, + + var displayStatus: String? = null ) diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt index a081e8e..1a04e32 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 @@ -75,6 +75,7 @@ import com.alya.ecommerce_serang.data.api.response.store.product.UpdateProductRe import com.alya.ecommerce_serang.data.api.response.store.product.ViewStoreProductsResponse import com.alya.ecommerce_serang.data.api.response.store.GenericResponse import com.alya.ecommerce_serang.data.api.response.store.profile.StoreDataResponse +import com.alya.ecommerce_serang.data.api.response.store.review.ProductReviewResponse import com.alya.ecommerce_serang.data.api.response.store.topup.BalanceTopUpResponse import com.alya.ecommerce_serang.data.api.response.store.topup.TopUpResponse import okhttp3.MultipartBody @@ -507,4 +508,8 @@ interface ApiService { @GET("mystore/notification") suspend fun getNotifStore( ): Response + + @GET("store/reviews") + suspend fun getStoreProductReview( + ): Response } \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/repository/ReviewRepository.kt b/app/src/main/java/com/alya/ecommerce_serang/data/repository/ReviewRepository.kt new file mode 100644 index 0000000..b443a3b --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/data/repository/ReviewRepository.kt @@ -0,0 +1,50 @@ +package com.alya.ecommerce_serang.data.repository + +import com.alya.ecommerce_serang.data.api.dto.ProductsItem +import com.alya.ecommerce_serang.data.api.response.customer.product.ProductResponse +import com.alya.ecommerce_serang.data.api.response.store.review.ProductReviewResponse +import com.alya.ecommerce_serang.data.api.retrofit.ApiService + +class ReviewRepository(private val apiService: ApiService) { + suspend fun getReviewList(score: String): Result { + return try { + val response = apiService.getStoreProductReview() + + if (response.isSuccessful) { + val allReviews = response.body() + val filteredReviews = if (score == "all") { + allReviews + } else { + val targetScore = score.toIntOrNull() + allReviews?.copy(reviews = allReviews.reviews?.filter { + val rating = it?.rating ?: 0 + when(targetScore) { + 5 -> rating > 4 + 4 -> rating > 3 && rating <= 4 + 3 -> rating > 2 && rating <= 3 + 2 -> rating > 1 && rating <= 2 + 1 -> rating <= 1 + else -> true + } + }) + } + Result.Success(filteredReviews!!) + } else { + Result.Error(Exception("HTTP ${response.code()}: ${response.message()}")) + } + } catch (e: Exception) { + Result.Error(e) + } + } + + suspend fun getProductDetail(productId: Int): ProductResponse? { + return try { + val response = apiService.getDetailProduct(productId) + if (response.isSuccessful) { + response.body() + } else null + } catch (e: Exception) { + null + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/ProfileFragment.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/ProfileFragment.kt index 25b7a0f..bf15129 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/ProfileFragment.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/ProfileFragment.kt @@ -16,15 +16,18 @@ import com.alya.ecommerce_serang.BuildConfig.BASE_URL import com.alya.ecommerce_serang.R import com.alya.ecommerce_serang.data.api.dto.UserProfile import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig +import com.alya.ecommerce_serang.data.repository.MyStoreRepository import com.alya.ecommerce_serang.data.repository.UserRepository import com.alya.ecommerce_serang.databinding.FragmentProfileBinding import com.alya.ecommerce_serang.ui.auth.LoginActivity -import com.alya.ecommerce_serang.ui.auth.RegisterStoreActivity +import com.alya.ecommerce_serang.ui.profile.mystore.RegisterStoreActivity import com.alya.ecommerce_serang.ui.order.address.AddressActivity import com.alya.ecommerce_serang.ui.order.history.HistoryActivity import com.alya.ecommerce_serang.ui.profile.mystore.MyStoreActivity +import com.alya.ecommerce_serang.ui.profile.mystore.StoreOnReviewActivity import com.alya.ecommerce_serang.utils.BaseViewModelFactory import com.alya.ecommerce_serang.utils.SessionManager +import com.alya.ecommerce_serang.utils.viewmodel.MyStoreViewModel import com.alya.ecommerce_serang.utils.viewmodel.ProfileViewModel import com.bumptech.glide.Glide import kotlinx.coroutines.delay @@ -44,6 +47,14 @@ class ProfileFragment : Fragment() { } } + private val myStoreViewModel: MyStoreViewModel by viewModels { + BaseViewModelFactory { + val apiService = ApiConfig.getApiService(sessionManager) + val myStoreRepository = MyStoreRepository(apiService) + MyStoreViewModel(myStoreRepository) + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) sessionManager = SessionManager(requireContext()) @@ -59,26 +70,32 @@ class ProfileFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + observeUserProfile() + viewModel.loadUserProfile() viewModel.checkStoreUser() - + val hasStore = viewModel.checkStore.value + Log.d("Profile Fragment", "Check store $hasStore") + binding.tvBukaToko.text = if (hasStore == true) "Toko Saya" else "Buka Toko" binding.cardBukaToko.setOnClickListener{ - val hasStore = viewModel.checkStore.value - - Log.d("Profile Fragment", "Check store $hasStore") - - if (hasStore == true){ - binding.tvBukaToko.text = "Lihat Toko Saya" - val intentBuka = Intent(requireContext(), MyStoreActivity::class.java) - startActivity(intentBuka) - } else { - binding.tvBukaToko.text = "Buka Toko" - val intentBuka = Intent(requireContext(), RegisterStoreActivity::class.java) - startActivity(intentBuka) - } +// if (hasStore == true) startActivity(Intent(requireContext(), MyStoreActivity::class.java)) +// else startActivity(Intent(requireContext(), RegisterStoreActivity::class.java)) + if (viewModel.checkStore.value == true) { + myStoreViewModel.loadMyStore() + myStoreViewModel.myStoreProfile.observe(viewLifecycleOwner) { store -> + store?.let { + when (store.storeStatus) { + "active" -> startActivity(Intent(requireContext(), MyStoreActivity::class.java)) + else -> startActivity(Intent(requireContext(), StoreOnReviewActivity::class.java)) + } + } ?: run { + Toast.makeText(requireContext(), "Gagal memuat data toko", Toast.LENGTH_SHORT).show() + } + } + } else startActivity(Intent(requireContext(), RegisterStoreActivity::class.java)) } binding.btnDetailProfile.setOnClickListener{ @@ -96,15 +113,14 @@ class ProfileFragment : Fragment() { startActivity(intent) } - binding.cardLogout.setOnClickListener({ + binding.cardLogout.setOnClickListener{ logout() + } - }) - - binding.cardAddress.setOnClickListener({ + binding.cardAddress.setOnClickListener{ val intent = Intent(requireContext(), AddressActivity::class.java) startActivity(intent) - }) + } } private fun observeUserProfile() { 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 c2a9200..4b89434 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 @@ -20,6 +20,7 @@ import com.alya.ecommerce_serang.ui.profile.mystore.balance.BalanceActivity import com.alya.ecommerce_serang.ui.profile.mystore.chat.ChatListStoreActivity import com.alya.ecommerce_serang.ui.profile.mystore.product.ProductActivity import com.alya.ecommerce_serang.ui.profile.mystore.profile.DetailStoreProfileActivity +import com.alya.ecommerce_serang.ui.profile.mystore.review.ReviewActivity import com.alya.ecommerce_serang.ui.profile.mystore.review.ReviewFragment import com.alya.ecommerce_serang.ui.profile.mystore.sells.SellsActivity import com.alya.ecommerce_serang.utils.BaseViewModelFactory @@ -105,25 +106,21 @@ class MyStoreActivity : AppCompatActivity() { } binding.tvHistory.setOnClickListener { - val intent = Intent(this, SellsActivity::class.java) - startActivity(intent) + startActivity(Intent(this, SellsActivity::class.java)) } binding.layoutPerluTagihan.setOnClickListener { - val intent = Intent(this, SellsActivity::class.java) - startActivity(intent) + startActivity(Intent(this, SellsActivity::class.java)) //navigateToSellsFragment("pending") } binding.layoutPembayaran.setOnClickListener { - val intent = Intent(this, SellsActivity::class.java) - startActivity(intent) + startActivity(Intent(this, SellsActivity::class.java)) //navigateToSellsFragment("paid") } binding.layoutPerluDikirim.setOnClickListener { - val intent = Intent(this, SellsActivity::class.java) - startActivity(intent) + startActivity(Intent(this, SellsActivity::class.java)) //navigateToSellsFragment("processed") } @@ -132,15 +129,11 @@ class MyStoreActivity : AppCompatActivity() { } binding.layoutReview.setOnClickListener { - supportFragmentManager.beginTransaction() - .replace(android.R.id.content, ReviewFragment()) - .addToBackStack(null) - .commit() + startActivity(Intent(this, ReviewActivity::class.java)) } binding.layoutInbox.setOnClickListener { - val intent = Intent(this, ChatListStoreActivity::class.java) - startActivity(intent) + startActivity(Intent(this, ChatListStoreActivity::class.java)) } } diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/auth/RegisterStoreActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/RegisterStoreActivity.kt similarity index 88% rename from app/src/main/java/com/alya/ecommerce_serang/ui/auth/RegisterStoreActivity.kt rename to app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/RegisterStoreActivity.kt index 32e36b5..a383441 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/auth/RegisterStoreActivity.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/RegisterStoreActivity.kt @@ -1,7 +1,6 @@ -package com.alya.ecommerce_serang.ui.auth +package com.alya.ecommerce_serang.ui.profile.mystore import android.Manifest -import android.app.Activity import android.content.Intent import android.content.pm.PackageManager import android.net.Uri @@ -26,6 +25,7 @@ import androidx.core.content.ContextCompat import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat +import com.alya.ecommerce_serang.R 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.repository.Result @@ -35,6 +35,9 @@ import com.alya.ecommerce_serang.ui.order.address.CityAdapter import com.alya.ecommerce_serang.ui.order.address.ProvinceAdapter import com.alya.ecommerce_serang.utils.BaseViewModelFactory import com.alya.ecommerce_serang.utils.SessionManager +import com.alya.ecommerce_serang.utils.viewmodel.RegisterStoreViewModel +import androidx.core.graphics.drawable.toDrawable +import androidx.core.widget.ImageViewCompat class RegisterStoreActivity : AppCompatActivity() { @@ -48,15 +51,13 @@ class RegisterStoreActivity : AppCompatActivity() { private val PICK_KTP_REQUEST = 1002 private val PICK_NPWP_REQUEST = 1003 private val PICK_NIB_REQUEST = 1004 - private val PICK_PERSETUJUAN_REQUEST = 1005 - private val PICK_QRIS_REQUEST = 1006 // Location request code private val LOCATION_PERMISSION_REQUEST = 2001 private val viewModel: RegisterStoreViewModel by viewModels { BaseViewModelFactory { - val apiService = ApiConfig.getApiService(sessionManager) + val apiService = ApiConfig.Companion.getApiService(sessionManager) val orderRepository = UserRepository(apiService) RegisterStoreViewModel(orderRepository) } @@ -84,6 +85,8 @@ class RegisterStoreActivity : AppCompatActivity() { windowInsets } + setupHeader() + provinceAdapter = ProvinceAdapter(this) cityAdapter = CityAdapter(this) Log.d(TAG, "onCreate: Adapters initialized") @@ -114,6 +117,9 @@ class RegisterStoreActivity : AppCompatActivity() { Log.d(TAG, "onCreate: Fetching provinces from API") viewModel.getProvinces() + viewModel.provinceId.observe(this) { validateRequiredFields() } + viewModel.cityId.observe(this) { validateRequiredFields() } + viewModel.storeTypeId.observe(this) { validateRequiredFields() } // Setup register button binding.btnRegister.setOnClickListener { @@ -130,6 +136,45 @@ class RegisterStoreActivity : AppCompatActivity() { Log.d(TAG, "onCreate: RegisterStoreActivity setup completed") } + private fun setupHeader() { + binding.header.main.background = ContextCompat.getColor(this, R.color.blue_500).toDrawable() + binding.header.headerTitle.visibility = View.GONE + binding.header.headerLeftIcon.setColorFilter( + ContextCompat.getColor(this, R.color.white), + android.graphics.PorterDuff.Mode.SRC_IN + ) + binding.header.headerLeftIcon.setOnClickListener { + onBackPressedDispatcher.onBackPressed() + finish() + } + } + + private fun validateRequiredFields() { + val isFormValid = !viewModel.storeName.value.isNullOrBlank() && + !viewModel.street.value.isNullOrBlank() && + (viewModel.postalCode.value ?: 0) > 0 && + !viewModel.subdistrict.value.isNullOrBlank() && + !viewModel.bankName.value.isNullOrBlank() && + (viewModel.bankNumber.value ?: 0) > 0 && + (viewModel.provinceId.value ?: 0) > 0 && + (viewModel.cityId.value ?: 0) > 0 && + (viewModel.storeTypeId.value ?: 0) > 0 && + viewModel.ktpUri != null && + viewModel.nibUri != null && + viewModel.npwpUri != null && + viewModel.selectedCouriers.isNotEmpty() + + binding.btnRegister.isEnabled = isFormValid + + if (isFormValid) { + binding.btnRegister.setBackgroundResource(R.drawable.bg_button_active) + binding.btnRegister.setTextColor(ContextCompat.getColor(this, R.color.white)) + } else { + binding.btnRegister.setBackgroundResource(R.drawable.bg_button_disabled) + binding.btnRegister.setTextColor(ContextCompat.getColor(this, R.color.black_300)) + } + } + private fun setupObservers() { Log.d(TAG, "setupObservers: Setting up LiveData observers") @@ -138,12 +183,12 @@ class RegisterStoreActivity : AppCompatActivity() { when (state) { is Result.Loading -> { Log.d(TAG, "setupObservers: Loading provinces...") - binding.provinceProgressBar?.visibility = View.VISIBLE + binding.provinceProgressBar.visibility = View.VISIBLE binding.spinnerProvince.isEnabled = false } is Result.Success -> { Log.d(TAG, "setupObservers: Provinces loaded successfully: ${state.data.size} provinces") - binding.provinceProgressBar?.visibility = View.GONE + binding.provinceProgressBar.visibility = View.GONE binding.spinnerProvince.isEnabled = true // Update adapter with data @@ -151,7 +196,7 @@ class RegisterStoreActivity : AppCompatActivity() { } is Result.Error -> { Log.e(TAG, "setupObservers: Error loading provinces: ${state.exception.message}") - binding.provinceProgressBar?.visibility = View.GONE + binding.provinceProgressBar.visibility = View.GONE binding.spinnerProvince.isEnabled = true } } @@ -162,12 +207,12 @@ class RegisterStoreActivity : AppCompatActivity() { when (state) { is Result.Loading -> { Log.d(TAG, "setupObservers: Loading cities...") - binding.cityProgressBar?.visibility = View.VISIBLE + binding.cityProgressBar.visibility = View.VISIBLE binding.spinnerCity.isEnabled = false } is Result.Success -> { Log.d(TAG, "setupObservers: Cities loaded successfully: ${state.data.size} cities") - binding.cityProgressBar?.visibility = View.GONE + binding.cityProgressBar.visibility = View.GONE binding.spinnerCity.isEnabled = true // Update adapter with data @@ -175,7 +220,7 @@ class RegisterStoreActivity : AppCompatActivity() { } is Result.Error -> { Log.e(TAG, "setupObservers: Error loading cities: ${state.exception.message}") - binding.cityProgressBar?.visibility = View.GONE + binding.cityProgressBar.visibility = View.GONE binding.spinnerCity.isEnabled = true } } @@ -214,11 +259,11 @@ class RegisterStoreActivity : AppCompatActivity() { Log.d(TAG, "setupStoreTypesObserver: Loading store types...") // Show loading indicator for store types spinner binding.spinnerStoreType.isEnabled = false - binding.storeTypeProgressBar?.visibility = View.VISIBLE + binding.storeTypeProgressBar.visibility = View.VISIBLE } else { Log.d(TAG, "setupStoreTypesObserver: Store types loading completed") binding.spinnerStoreType.isEnabled = true - binding.storeTypeProgressBar?.visibility = View.GONE + binding.storeTypeProgressBar.visibility = View.GONE } } @@ -309,7 +354,7 @@ class RegisterStoreActivity : AppCompatActivity() { } // Hide progress bar after setup - binding.storeTypeProgressBar?.visibility = View.GONE + binding.storeTypeProgressBar.visibility = View.GONE Log.d(TAG, "setupStoreTypeSpinner: Store type spinner setup completed") } @@ -398,23 +443,11 @@ class RegisterStoreActivity : AppCompatActivity() { } // NPWP - binding.containerNpwp?.setOnClickListener { + binding.containerNpwp.setOnClickListener { Log.d(TAG, "NPWP container clicked, picking image") pickImage(PICK_NPWP_REQUEST) } - // SPPIRT - binding.containerSppirt.setOnClickListener { - Log.d(TAG, "SPPIRT container clicked, picking document") - pickDocument(PICK_PERSETUJUAN_REQUEST) - } - - // Halal - binding.containerHalal.setOnClickListener { - Log.d(TAG, "Halal container clicked, picking document") - pickDocument(PICK_QRIS_REQUEST) - } - Log.d(TAG, "setupDocumentUploads: Document upload buttons setup completed") } @@ -442,16 +475,16 @@ class RegisterStoreActivity : AppCompatActivity() { handleCourierSelection("jne", isChecked) } - binding.checkboxJnt.setOnCheckedChangeListener { _, isChecked -> - Log.d(TAG, "JNT checkbox ${if (isChecked) "checked" else "unchecked"}") - handleCourierSelection("tiki", isChecked) - } - binding.checkboxPos.setOnCheckedChangeListener { _, isChecked -> Log.d(TAG, "POS checkbox ${if (isChecked) "checked" else "unchecked"}") handleCourierSelection("pos", isChecked) } + binding.checkboxTiki.setOnCheckedChangeListener { _, isChecked -> + Log.d(TAG, "TIKI checkbox ${if (isChecked) "checked" else "unchecked"}") + handleCourierSelection("tiki", isChecked) + } + Log.d(TAG, "setupCourierSelection: Courier checkboxes setup completed") } @@ -465,6 +498,7 @@ class RegisterStoreActivity : AppCompatActivity() { viewModel.selectedCouriers.remove(courier) Log.d(TAG, "handleCourierSelection: Removed courier: $courier. Current couriers: ${viewModel.selectedCouriers}") } + validateRequiredFields() } private fun setupMap() { @@ -516,6 +550,7 @@ class RegisterStoreActivity : AppCompatActivity() { override fun afterTextChanged(s: Editable?) { viewModel.storeName.value = s.toString() Log.d(TAG, "Store name updated: ${s.toString()}") + validateRequiredFields() } }) @@ -534,6 +569,7 @@ class RegisterStoreActivity : AppCompatActivity() { override fun afterTextChanged(s: Editable?) { viewModel.street.value = s.toString() Log.d(TAG, "Street address updated: ${s.toString()}") + validateRequiredFields() } }) @@ -547,6 +583,7 @@ class RegisterStoreActivity : AppCompatActivity() { } catch (e: NumberFormatException) { // Handle invalid input Log.e(TAG, "Invalid postal code input: ${s.toString()}, error: $e") + validateRequiredFields() } } }) @@ -578,6 +615,7 @@ class RegisterStoreActivity : AppCompatActivity() { viewModel.bankNumber.value = 0 // or 0 Log.d(TAG, "Bank number set to default: 0") } + validateRequiredFields() } }) @@ -587,6 +625,7 @@ class RegisterStoreActivity : AppCompatActivity() { override fun afterTextChanged(s: Editable?) { viewModel.subdistrict.value = s.toString() Log.d(TAG, "Subdistrict updated: ${s.toString()}") + validateRequiredFields() } }) @@ -596,6 +635,7 @@ class RegisterStoreActivity : AppCompatActivity() { override fun afterTextChanged(s: Editable?) { viewModel.bankName.value = s.toString() Log.d(TAG, "Bank name updated: ${s.toString()}") + validateRequiredFields() } }) @@ -605,7 +645,7 @@ class RegisterStoreActivity : AppCompatActivity() { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) Log.d(TAG, "onActivityResult: Request code: $requestCode, Result code: $resultCode") - if (resultCode == Activity.RESULT_OK && data != null) { + if (resultCode == RESULT_OK && data != null) { val uri = data.data Log.d(TAG, "onActivityResult: URI received: $uri") when (requestCode) { @@ -618,26 +658,19 @@ class RegisterStoreActivity : AppCompatActivity() { Log.d(TAG, "KTP image selected") viewModel.ktpUri = uri updateImagePreview(uri, binding.imgKtp, binding.layoutUploadKtp) + validateRequiredFields() } PICK_NPWP_REQUEST -> { Log.d(TAG, "NPWP document selected") viewModel.npwpUri = uri updateDocumentPreview(binding.layoutUploadNpwp) + validateRequiredFields() } PICK_NIB_REQUEST -> { Log.d(TAG, "NIB document selected") viewModel.nibUri = uri updateDocumentPreview(binding.layoutUploadNib) - } - PICK_PERSETUJUAN_REQUEST -> { - Log.d(TAG, "SPPIRT document selected") - viewModel.persetujuanUri = uri - updateDocumentPreview(binding.layoutUploadSppirt) - } - PICK_QRIS_REQUEST -> { - Log.d(TAG, "Halal document selected") - viewModel.qrisUri = uri - updateDocumentPreview(binding.layoutUploadHalal) + validateRequiredFields() } else -> { Log.w(TAG, "Unknown request code: $requestCode") diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/StoreOnReviewActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/StoreOnReviewActivity.kt new file mode 100644 index 0000000..338e5a5 --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/StoreOnReviewActivity.kt @@ -0,0 +1,25 @@ +package com.alya.ecommerce_serang.ui.profile.mystore + +import android.os.Bundle +import androidx.activity.enableEdgeToEdge +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import com.alya.ecommerce_serang.R +import com.alya.ecommerce_serang.databinding.ActivityStoreOnReviewBinding + +class StoreOnReviewActivity : AppCompatActivity() { + private lateinit var binding: ActivityStoreOnReviewBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityStoreOnReviewBinding.inflate(layoutInflater) + setContentView(binding.root) + + binding.header.headerTitle.text = "Verifikasi Pengajuan Toko" + binding.header.headerLeftIcon.setOnClickListener { + onBackPressedDispatcher.onBackPressed() + finish() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/ChangePriceBottomSheetFragment.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/ChangePriceBottomSheetFragment.kt index 0d8be50..8a88ac1 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/ChangePriceBottomSheetFragment.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/ChangePriceBottomSheetFragment.kt @@ -5,56 +5,47 @@ import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Button +import android.widget.EditText +import android.widget.Toast import com.alya.ecommerce_serang.R +import com.alya.ecommerce_serang.data.api.dto.ProductsItem +import com.alya.ecommerce_serang.databinding.FragmentChangePriceBottomSheetBinding +import com.google.android.material.bottomsheet.BottomSheetDialogFragment -// TODO: Rename parameter arguments, choose names that match -// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER -private const val ARG_PARAM1 = "param1" -private const val ARG_PARAM2 = "param2" - -/** - * A simple [Fragment] subclass. - * Use the [ChangePriceBottomSheetFragment.newInstance] factory method to - * create an instance of this fragment. - */ -class ChangePriceBottomSheetFragment : Fragment() { - // TODO: Rename and change types of parameters - private var param1: String? = null - private var param2: String? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - arguments?.let { - param1 = it.getString(ARG_PARAM1) - param2 = it.getString(ARG_PARAM2) - } - } +class ChangePriceBottomSheetFragment( + private val product: ProductsItem, + private val onSave: (productId: Int, newPrice: Int) -> Unit +) : BottomSheetDialogFragment() { + private var _binding: FragmentChangePriceBottomSheetBinding? = null + private val binding get() = _binding!! override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { - // Inflate the layout for this fragment - return inflater.inflate(R.layout.fragment_change_price_bottom_sheet, container, false) + ): View { + _binding = FragmentChangePriceBottomSheetBinding.inflate(inflater, container, false) + + binding.header.headerTitle.text = "Atur Harga" + binding.header.headerLeftIcon.setImageResource(R.drawable.ic_close) + binding.header.headerLeftIcon.setOnClickListener { dismiss() } + + binding.edtPrice.setText(product.price) + + binding.btnSave.setOnClickListener { + val newPrice = binding.edtPrice.text.toString().replace(".", "").toIntOrNull() + if (newPrice != null && newPrice > 0) { + onSave(product.id, newPrice) + dismiss() + } else { + Toast.makeText(requireContext(), "Masukkan harga yang valid", Toast.LENGTH_SHORT).show() + } + } + return binding.root } - companion object { - /** - * Use this factory method to create a new instance of - * this fragment using the provided parameters. - * - * @param param1 Parameter 1. - * @param param2 Parameter 2. - * @return A new instance of fragment ChangePriceBottomSheetFragment. - */ - // TODO: Rename and change types and number of parameters - @JvmStatic - fun newInstance(param1: String, param2: String) = - ChangePriceBottomSheetFragment().apply { - arguments = Bundle().apply { - putString(ARG_PARAM1, param1) - putString(ARG_PARAM2, param2) - } - } + override fun onDestroyView() { + super.onDestroyView() + _binding = null } } \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/ChangeStockBottomSheetFragment.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/ChangeStockBottomSheetFragment.kt new file mode 100644 index 0000000..9a4762d --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/ChangeStockBottomSheetFragment.kt @@ -0,0 +1,69 @@ +package com.alya.ecommerce_serang.ui.profile.mystore.product + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import com.alya.ecommerce_serang.R +import com.alya.ecommerce_serang.data.api.dto.ProductsItem +import com.alya.ecommerce_serang.databinding.FragmentChangeStockBottomSheetBinding +import com.google.android.material.bottomsheet.BottomSheetDialogFragment + +class ChangeStockBottomSheetFragment( + private val product: ProductsItem, + private val onSave: (productId: Int, newStock: Int) -> Unit +): BottomSheetDialogFragment() { + private var _binding: FragmentChangeStockBottomSheetBinding? = null + private val binding get() = _binding!! + + private var stock = 0 + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + _binding = FragmentChangeStockBottomSheetBinding.inflate(inflater, container, false) + + binding.header.headerTitle.text = "Atur Stok" + binding.header.headerLeftIcon.setImageResource(R.drawable.ic_close) + binding.header.headerLeftIcon.setOnClickListener { dismiss() } + + stock = product.stock + updateStock() + + binding.btnMinus.setOnClickListener { + if (stock > 0) stock-- + updateStock() + } + + binding.btnPlus.setOnClickListener { + stock++ + updateStock() + } + + binding.btnSave.setOnClickListener { + onSave(product.id, stock) + dismiss() + } + + return binding.root + } + + private fun updateStock() { + binding.edtStock.setText(stock.toString()) + if (stock == 0) { + binding.btnMinus.isEnabled = false + binding.btnMinus.setColorFilter(ContextCompat.getColor(requireContext(), R.color.black_100)) + } else { + binding.btnMinus.isEnabled = true + binding.btnMinus.setColorFilter(ContextCompat.getColor(requireContext(), R.color.blue_500)) + } + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/ProductActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/ProductActivity.kt index 45a3cbd..e28b2f6 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/ProductActivity.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/ProductActivity.kt @@ -20,9 +20,10 @@ class ProductActivity : AppCompatActivity() { private lateinit var binding: ActivityProductBinding private lateinit var sessionManager: SessionManager + private lateinit var productAdapter: ProductAdapter + private val viewModel: ProductViewModel by viewModels { BaseViewModelFactory { - sessionManager = SessionManager(this) val apiService = ApiConfig.getApiService(sessionManager) val productRepository = ProductRepository(apiService) ProductViewModel(productRepository) @@ -30,6 +31,8 @@ class ProductActivity : AppCompatActivity() { } override fun onCreate(savedInstanceState: Bundle?) { + sessionManager = SessionManager(this) + super.onCreate(savedInstanceState) binding = ActivityProductBinding.inflate(layoutInflater) setContentView(binding.root) @@ -56,9 +59,18 @@ class ProductActivity : AppCompatActivity() { is Result.Success -> { binding.progressBar.visibility = View.GONE val products = result.data - binding.rvStoreProduct.adapter = ProductAdapter(products) { - Toast.makeText(this, "Produk: ${it.name}", Toast.LENGTH_SHORT).show() - } + + productAdapter = ProductAdapter( + products, + onItemClick = { products -> + Toast.makeText(this, "Produk ${products.name} diklik", Toast.LENGTH_SHORT).show() + }, + onUpdateProduct = { productId, updatedFields -> + viewModel.updateProduct(productId, updatedFields) + } + ) + + binding.rvStoreProduct.adapter = productAdapter } is Result.Error -> { binding.progressBar.visibility = View.GONE @@ -66,6 +78,19 @@ class ProductActivity : AppCompatActivity() { } } } + + viewModel.productUpdateResult.observe(this) { result -> + when (result) { + is Result.Success -> { + Toast.makeText(this, "Produk berhasil diperbarui", Toast.LENGTH_SHORT).show() + viewModel.loadMyStoreProducts() + } + is Result.Error -> { + Toast.makeText(this, "Gagal memperbarui produk", Toast.LENGTH_SHORT).show() + } + else -> {} + } + } } private fun setupHeader() { diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/ProductAdapter.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/ProductAdapter.kt index c249fb0..5fa810b 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/ProductAdapter.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/product/ProductAdapter.kt @@ -3,6 +3,7 @@ package com.alya.ecommerce_serang.ui.profile.mystore.product import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Button import android.widget.ImageView import android.widget.PopupMenu import android.widget.TextView @@ -15,10 +16,14 @@ import com.alya.ecommerce_serang.R import com.alya.ecommerce_serang.data.api.dto.Product import com.alya.ecommerce_serang.data.api.dto.ProductsItem import com.bumptech.glide.Glide +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.RequestBody +import okhttp3.RequestBody.Companion.toRequestBody class ProductAdapter( private val products: List, - private val onItemClick: (ProductsItem) -> Unit + private val onItemClick: (ProductsItem) -> Unit, + private val onUpdateProduct: (productId: Int, updatedFields: Map) -> Unit ) : RecyclerView.Adapter() { inner class ProductViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { @@ -28,6 +33,8 @@ class ProductAdapter( private val tvProductStock: TextView = itemView.findViewById(R.id.tv_product_stock) private val tvProductStatus: TextView = itemView.findViewById(R.id.tv_product_status) private val ivMenu: ImageView = itemView.findViewById(R.id.iv_menu) + private val btnChangePrice: Button = itemView.findViewById(R.id.btn_change_price) + private val btnChangeStock: Button = itemView.findViewById(R.id.btn_change_stock) fun bind(product: ProductsItem) { tvProductName.text = product.name @@ -55,7 +62,6 @@ class ProductAdapter( .into(ivProduct) ivMenu.setOnClickListener { - // Show Bottom Sheet when menu is clicked val bottomSheetFragment = ProductOptionsBottomSheetFragment(product) bottomSheetFragment.show( (itemView.context as FragmentActivity).supportFragmentManager, @@ -63,6 +69,36 @@ class ProductAdapter( ) } + btnChangePrice.setOnClickListener { + val bottomSheetFragment = ChangePriceBottomSheetFragment(product) { id, newPrice -> + val body = mapOf( + "product_id" to id.toString().toRequestBody("text/plain".toMediaTypeOrNull()), + "price" to newPrice.toString().toRequestBody("text/plain".toMediaTypeOrNull()) + ) + onUpdateProduct(id, body) + Toast.makeText(itemView.context, "Harga berhasil diubah", Toast.LENGTH_SHORT).show() + } + bottomSheetFragment.show( + (itemView.context as FragmentActivity).supportFragmentManager, + bottomSheetFragment.tag + ) + } + + btnChangeStock.setOnClickListener { + val bottomSheetFragment = ChangeStockBottomSheetFragment(product) { id, newStock -> + val body = mapOf( + "product_id" to id.toString().toRequestBody("text/plain".toMediaTypeOrNull()), + "stock" to newStock.toString().toRequestBody("text/plain".toMediaTypeOrNull()) + ) + onUpdateProduct(id, body) + Toast.makeText(itemView.context, "Stok berhasil diubah", Toast.LENGTH_SHORT).show() + } + bottomSheetFragment.show( + (itemView.context as FragmentActivity).supportFragmentManager, + bottomSheetFragment.tag + ) + } + itemView.setOnClickListener { onItemClick(product) } diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/review/ReviewActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/review/ReviewActivity.kt new file mode 100644 index 0000000..8d131ea --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/review/ReviewActivity.kt @@ -0,0 +1,74 @@ +package com.alya.ecommerce_serang.ui.profile.mystore.review + +import android.os.Bundle +import androidx.activity.enableEdgeToEdge +import androidx.activity.viewModels +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.fragment.app.commit +import com.alya.ecommerce_serang.R +import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig +import com.alya.ecommerce_serang.data.repository.ReviewRepository +import com.alya.ecommerce_serang.databinding.ActivityReviewBinding +import com.alya.ecommerce_serang.utils.BaseViewModelFactory +import com.alya.ecommerce_serang.utils.SessionManager +import com.alya.ecommerce_serang.utils.viewmodel.ReviewViewModel + +class ReviewActivity : AppCompatActivity() { + private lateinit var binding: ActivityReviewBinding + private lateinit var sessionManager: SessionManager + + private val viewModel: ReviewViewModel by viewModels { + BaseViewModelFactory { + val apiService = ApiConfig.getApiService(sessionManager) + val reviewRepository = ReviewRepository(apiService) + ReviewViewModel(reviewRepository) + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityReviewBinding.inflate(layoutInflater) + setContentView(binding.root) + + sessionManager = SessionManager(this) + + ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view, windowInsets -> + val systemBars = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) + view.setPadding( + systemBars.left, + systemBars.top, + systemBars.right, + systemBars.bottom + ) + windowInsets + } + + setupHeader() + + viewModel.getReview("all") + viewModel.averageScore.observe(this) { binding.tvReviewScore.text = it } + viewModel.totalReview.observe(this) { binding.tvTotalReview.text = "$it rating" } + viewModel.totalReviewWithDesc.observe(this) { binding.tvTotalReviewWithDesc.text = "$it ulasan" } + + if (savedInstanceState == null) { + showReviewFragment() + } + } + + private fun setupHeader() { + binding.header.headerTitle.text = "Ulasan Pembeli" + + binding.header.headerLeftIcon.setOnClickListener { + onBackPressed() + finish() + } + } + + private fun showReviewFragment() { + supportFragmentManager.commit { + replace(R.id.fragment_container_reviews, ReviewFragment()) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/review/ReviewAdapter.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/review/ReviewAdapter.kt new file mode 100644 index 0000000..5eab57f --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/review/ReviewAdapter.kt @@ -0,0 +1,81 @@ +package com.alya.ecommerce_serang.ui.profile.mystore.review + +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.alya.ecommerce_serang.R +import com.alya.ecommerce_serang.data.api.dto.ReviewsItem +import com.alya.ecommerce_serang.utils.viewmodel.ReviewViewModel +import com.bumptech.glide.Glide +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +class ReviewAdapter( + private val viewModel: ReviewViewModel +): RecyclerView.Adapter() { + + private val reviews = mutableListOf() + private var fragmentScore: String = "all" + + fun setFragmentScore(score: String) { + fragmentScore = score + } + + fun submitList(newReviews: List) { + reviews.clear() + reviews.addAll(newReviews) + notifyDataSetChanged() + } + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): ReviewAdapter.ReviewViewHolder { + val view = LayoutInflater.from(parent.context).inflate(R.layout.item_store_product_review, parent, false) + return ReviewViewHolder(view) + } + + override fun onBindViewHolder(holder: ReviewViewHolder, position: Int) { + if (position < reviews.size) { + holder.bind(reviews[position]) + } else { + Log.e("ReviewAdapter", "Position $position is out of bounds for size ${reviews.size}") + } + } + + override fun getItemCount(): Int = reviews.size + + inner class ReviewViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + private val ivProduct: ImageView = itemView.findViewById(R.id.iv_product) + private val tvProductName: TextView = itemView.findViewById(R.id.tv_product_name) + private val tvReviewScore: TextView = itemView.findViewById(R.id.tv_review_score) + private val tvReviewDate: TextView = itemView.findViewById(R.id.tv_review_date) + private val tvUsername: TextView = itemView.findViewById(R.id.tv_username) + private val tvReviewDesc: TextView = itemView.findViewById(R.id.tv_review_desc) + private val ivMenu: ImageView = itemView.findViewById(R.id.iv_menu) + + fun bind(review: ReviewsItem) { + val actualScore = + if (fragmentScore == "all") review.rating.toString() else fragmentScore + + CoroutineScope(Dispatchers.Main).launch { + val imageUrl = viewModel.getProductImage(review.productId ?: -1) + Glide.with(itemView.context) + .load(imageUrl) + .placeholder(R.drawable.placeholder_image) + .into(ivProduct) + } + + tvProductName.text = review.productName + tvReviewScore.text = actualScore + tvReviewDate.text = review.reviewDate + tvUsername.text = review.username + tvReviewDesc.text = review.reviewText + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/review/ReviewFragment.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/review/ReviewFragment.kt index 45e553b..0b9e947 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/review/ReviewFragment.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/review/ReviewFragment.kt @@ -1,32 +1,56 @@ package com.alya.ecommerce_serang.ui.profile.mystore.review -import androidx.fragment.app.viewModels import android.os.Bundle import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import com.alya.ecommerce_serang.R -import com.alya.ecommerce_serang.utils.viewmodel.ReviewViewModel +import com.alya.ecommerce_serang.databinding.FragmentReviewBinding +import com.alya.ecommerce_serang.utils.SessionManager +import com.google.android.material.tabs.TabLayoutMediator class ReviewFragment : Fragment() { - companion object { - fun newInstance() = ReviewFragment() - } + private var _binding: FragmentReviewBinding? = null + private val binding get() = _binding!! + private lateinit var sessionManager: SessionManager - private val viewModel: ReviewViewModel by viewModels() - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - // TODO: Use the ViewModel - } + private lateinit var viewPagerAdapter: ReviewViewPagerAdapter override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { - return inflater.inflate(R.layout.fragment_review, container, false) + _binding = FragmentReviewBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + sessionManager = SessionManager(requireContext()) + + setupViewPager() + } + + private fun setupViewPager() { + viewPagerAdapter = ReviewViewPagerAdapter(requireActivity()) + binding.viewPagerReview.adapter = viewPagerAdapter + + TabLayoutMediator(binding.tabLayoutReview, binding.viewPagerReview) { tab, position -> + tab.text = when (position) { + 0 -> "Semua" + 1 -> "5 Bintang" + 2 -> "4 Bintang" + 3 -> "3 Bintang" + 4 -> "2 Bintang" + 5 -> "1 Bintang" + else -> "Tab $position" + } + }.attach() + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null } } \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/review/ReviewListFragment.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/review/ReviewListFragment.kt new file mode 100644 index 0000000..029d873 --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/review/ReviewListFragment.kt @@ -0,0 +1,123 @@ +package com.alya.ecommerce_serang.ui.profile.mystore.review + +import android.os.Bundle +import android.util.Log +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.viewModels +import androidx.recyclerview.widget.LinearLayoutManager +import com.alya.ecommerce_serang.R +import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig +import com.alya.ecommerce_serang.data.repository.ReviewRepository +import com.alya.ecommerce_serang.databinding.FragmentReviewListBinding +import com.alya.ecommerce_serang.ui.order.address.ViewState +import com.alya.ecommerce_serang.utils.BaseViewModelFactory +import com.alya.ecommerce_serang.utils.SessionManager +import com.alya.ecommerce_serang.utils.viewmodel.ProductViewModel +import com.alya.ecommerce_serang.utils.viewmodel.ReviewViewModel + +class ReviewListFragment : Fragment() { + + private var _binding: FragmentReviewListBinding? = null + private val binding get() = _binding!! + private lateinit var sessionManager: SessionManager + + private lateinit var reviewAdapter: ReviewAdapter + private val viewModel: ReviewViewModel by viewModels { + BaseViewModelFactory { + val apiService = ApiConfig.getApiService(SessionManager(requireContext())) + ReviewViewModel(ReviewRepository(apiService)) + } + } + + private var score: String = "all" + + companion object { + private const val ARG_SCORE = "score" + + fun newInstance(score: String): ReviewListFragment = ReviewListFragment().apply { + arguments = Bundle().apply { + putString(ARG_SCORE, score) + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + sessionManager = SessionManager(requireContext()) + score = arguments?.getString(ARG_SCORE) ?: "all" + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + _binding = FragmentReviewListBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + reviewAdapter = ReviewAdapter(viewModel) + binding.rvReview.apply { + layoutManager = LinearLayoutManager(requireContext()) + adapter = reviewAdapter + } + + observeReviewList() + fetchReviewByScore(score) + } + + private fun fetchReviewByScore(score: String) { + val normalizedScore = when (score) { + "all" -> "all" + else -> { + val scoreValue = score.toDoubleOrNull() ?: 0.0 + when { + scoreValue > 4.5 -> "5" + scoreValue > 3.5 -> "4" + scoreValue > 2.5 -> "3" + scoreValue > 1.5 -> "2" + else -> "1" + } + } + } + viewModel.getReview(normalizedScore) + } + + private fun observeReviewList() { + viewModel.review.observe(viewLifecycleOwner) { result -> + when (result) { + is ViewState.Success -> { + val data = result.data.orEmpty().sortedByDescending { it.reviewDate } + binding.progressBar.visibility = View.GONE + + if (data.isEmpty()) { + binding.tvEmptyState.visibility = View.VISIBLE + binding.rvReview.visibility = View.GONE + } else { + binding.tvEmptyState.visibility = View.GONE + binding.rvReview.visibility = View.VISIBLE + reviewAdapter.submitList(data) + } + } + + is ViewState.Loading -> binding.progressBar.visibility = View.VISIBLE + is ViewState.Error -> { + binding.progressBar.visibility = View.GONE + binding.tvEmptyState.visibility = View.VISIBLE + Toast.makeText(requireContext(), result.message, Toast.LENGTH_SHORT).show() + } + } + } + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/review/ReviewViewPagerAdapter.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/review/ReviewViewPagerAdapter.kt new file mode 100644 index 0000000..8973690 --- /dev/null +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/review/ReviewViewPagerAdapter.kt @@ -0,0 +1,24 @@ +package com.alya.ecommerce_serang.ui.profile.mystore.review + +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.viewpager2.adapter.FragmentStateAdapter + +class ReviewViewPagerAdapter( + fragmentActivity: FragmentActivity +) : FragmentStateAdapter(fragmentActivity) { + private val reviewScore = listOf( + "all", + "5", + "4", + "3", + "2", + "1" + ) + + override fun getItemCount(): Int = reviewScore.size + + override fun createFragment(position: Int): Fragment { + return ReviewListFragment.newInstance(reviewScore[position]) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsAdapter.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsAdapter.kt index 4fadaab..38342be 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsAdapter.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsAdapter.kt @@ -86,7 +86,7 @@ class SellsAdapter( Log.d("SellsAdapter", "=== ViewHolder.bind() called ===") Log.d("SellsAdapter", "Binding order: ${order.orderId} with status: ${order.status}") - val actualStatus = if (fragmentStatus == "all") order.status ?: "" else fragmentStatus + val actualStatus = if (fragmentStatus == "all") order.displayStatus ?: "" else fragmentStatus adjustDisplay(actualStatus, order) tvSellsNumber.text = "No. Pesanan: ${order.orderId}" diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipment/DetailShipmentActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipment/DetailShipmentActivity.kt index 9ca1b03..99b8077 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipment/DetailShipmentActivity.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/shipment/DetailShipmentActivity.kt @@ -6,6 +6,7 @@ import android.util.Log import android.widget.Toast import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity +import androidx.recyclerview.widget.LinearLayoutManager import com.alya.ecommerce_serang.data.api.response.store.sells.Orders import com.alya.ecommerce_serang.data.api.response.store.sells.OrdersItem import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig @@ -13,6 +14,7 @@ import com.alya.ecommerce_serang.data.repository.AddressRepository import com.alya.ecommerce_serang.data.repository.SellsRepository import com.alya.ecommerce_serang.databinding.ActivityDetailShipmentBinding import com.alya.ecommerce_serang.ui.profile.mystore.sells.SellsProductAdapter +import com.alya.ecommerce_serang.ui.profile.mystore.sells.payment.DetailPaymentActivity import com.alya.ecommerce_serang.utils.BaseViewModelFactory import com.alya.ecommerce_serang.utils.SessionManager import com.alya.ecommerce_serang.utils.viewmodel.AddressViewModel @@ -51,6 +53,12 @@ class DetailShipmentActivity : AppCompatActivity() { finish() } + productAdapter = SellsProductAdapter() + binding.rvProductItems.apply { + adapter = productAdapter + layoutManager = LinearLayoutManager(this@DetailShipmentActivity) + } + val sellsJson = intent.getStringExtra("sells_data") if (sellsJson != null) { try { diff --git a/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/ProductViewModel.kt b/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/ProductViewModel.kt index 280f3e1..61c796f 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/ProductViewModel.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/ProductViewModel.kt @@ -111,9 +111,9 @@ class ProductViewModel(private val repository: ProductRepository) : ViewModel() fun updateProduct( productId: Int?, data: Map, - image: MultipartBody.Part?, - halal: MultipartBody.Part?, - sppirt: MultipartBody.Part? + image: MultipartBody.Part? = null, + halal: MultipartBody.Part? = null, + sppirt: MultipartBody.Part? = null ) { viewModelScope.launch { _productUpdateResult.postValue(Result.Loading) diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/auth/RegisterStoreViewModel.kt b/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/RegisterStoreViewModel.kt similarity index 96% rename from app/src/main/java/com/alya/ecommerce_serang/ui/auth/RegisterStoreViewModel.kt rename to app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/RegisterStoreViewModel.kt index b29fa41..ae9b8f4 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/auth/RegisterStoreViewModel.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/RegisterStoreViewModel.kt @@ -1,4 +1,4 @@ -package com.alya.ecommerce_serang.ui.auth +package com.alya.ecommerce_serang.utils.viewmodel import android.content.Context import android.net.Uri @@ -21,8 +21,8 @@ class RegisterStoreViewModel( ) : ViewModel() { // LiveData for UI state - private val _registerState = MutableLiveData>() - val registerState: LiveData> = _registerState + private val _registerState = MutableLiveData>() + val registerState: LiveData> = _registerState private val _storeTypes = MutableLiveData>() val storeTypes: LiveData> = _storeTypes @@ -141,7 +141,7 @@ class RegisterStoreViewModel( _registerState.value = result } catch (e: Exception) { - _registerState.value = com.alya.ecommerce_serang.data.repository.Result.Error(e) + _registerState.value = Result.Error(e) } } } diff --git a/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/ReviewViewModel.kt b/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/ReviewViewModel.kt index 3959502..2493692 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/ReviewViewModel.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/ReviewViewModel.kt @@ -1,7 +1,71 @@ package com.alya.ecommerce_serang.utils.viewmodel +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.alya.ecommerce_serang.BuildConfig.BASE_URL +import com.alya.ecommerce_serang.data.api.dto.ReviewsItem +import com.alya.ecommerce_serang.data.repository.Result +import com.alya.ecommerce_serang.data.repository.ReviewRepository +import com.alya.ecommerce_serang.ui.order.address.ViewState +import kotlinx.coroutines.launch +import kotlin.getOrThrow -class ReviewViewModel : ViewModel() { - // TODO: Implement the ViewModel +class ReviewViewModel(private val repository: ReviewRepository) : ViewModel() { + + private val _review = MutableLiveData>>() + val review: LiveData>> = _review + + private val _averageScore = MutableLiveData() + val averageScore: LiveData = _averageScore + + private val _totalReview = MutableLiveData() + val totalReview: LiveData = _totalReview + + private val _totalReviewWithDesc = MutableLiveData() + val totalReviewWithDesc: LiveData = _totalReviewWithDesc + + private val _isLoading = MutableLiveData() + val isLoading: LiveData = _isLoading + + private val productImageCache = mutableMapOf() + + fun getReview(score: String) { + _review.value = ViewState.Loading + viewModelScope.launch { + try { + val response = repository.getReviewList(score) + if (response is Result.Success) { + val reviews = response.data.reviews?.filterNotNull().orEmpty() + _review.value = ViewState.Success(reviews) + + if (score == "all") { + val avg = if (reviews.isNotEmpty()) { + reviews.mapNotNull { it.rating }.average() + } else 0.0 + _averageScore.value = String.format("%.1f", avg) + _totalReview.value = reviews.size + _totalReviewWithDesc.value = reviews.count { !it.reviewText.isNullOrBlank() } + } + } else if (response is Result.Error) { + _review.value = ViewState.Error(response.exception.message ?: "Gagal memuat ulasan") + } + } catch (e: Exception) { + _review.value = ViewState.Error(e.message ?: "Terjadi kesalahan") + } + } + } + + suspend fun getProductImage(productId: Int): String? { + if (productImageCache.containsKey(productId)) { + return productImageCache[productId] + } + val result = repository.getProductDetail(productId) + val imageUrl = if (result?.product?.image?.startsWith("/") == true) { + BASE_URL + result.product.image.removePrefix("/") + } else result?.product?.image + productImageCache[productId] = imageUrl.toString() + return imageUrl.toString() + } } \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/SellsViewModel.kt b/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/SellsViewModel.kt index c5c0d73..05a26aa 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/SellsViewModel.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/utils/viewmodel/SellsViewModel.kt @@ -14,7 +14,12 @@ import com.alya.ecommerce_serang.data.api.response.store.sells.PaymentConfirmati import com.alya.ecommerce_serang.data.repository.Result import com.alya.ecommerce_serang.data.repository.SellsRepository import com.alya.ecommerce_serang.ui.order.address.ViewState +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch +import java.text.SimpleDateFormat +import java.util.Locale class SellsViewModel(private val repository: SellsRepository) : ViewModel() { @@ -59,79 +64,128 @@ class SellsViewModel(private val repository: SellsRepository) : ViewModel() { Log.d(TAG, "Coroutine launched successfully") try { - Log.d(TAG, "Calling repository.getSellList(status='$status')") - val startTime = System.currentTimeMillis() + if(status == "all") { + Log.d(TAG, "Status is 'all', calling repository.getSellList()") + val allStatuses = listOf("pending", "unpaid", "processed", "shipped") + val allSells = mutableListOf() - when (val result = repository.getSellList(status)) { - is Result.Success -> { - val endTime = System.currentTimeMillis() - Log.d(TAG, "Repository call completed in ${endTime - startTime}ms") - Log.d(TAG, "Result.Success received from repository") - - // Log the entire result data structure - Log.d(TAG, "Raw result data: ${result.data}") - Log.d(TAG, "Result data class: ${result.data.javaClass.simpleName}") - - val orders = result.data.orders - Log.d(TAG, "Extracted orders list: $orders") - Log.d(TAG, "Orders list class: ${orders.javaClass.simpleName}") - Log.d(TAG, "Orders count: ${orders.size}") - - // Check if orders list is null or empty - if (false) { - Log.w(TAG, "⚠️ Orders list is NULL") - } else if (orders.isEmpty()) { - Log.w(TAG, "⚠️ Orders list is EMPTY") - } else { - Log.d(TAG, "✅ Orders list contains ${orders.size} items") - - // Log individual order details with more comprehensive info - orders.forEachIndexed { index, order -> - Log.d(TAG, "--- Order ${index + 1}/${orders.size} ---") - Log.d(TAG, " Order object: $order") - Log.d(TAG, " Order class: ${order.javaClass.simpleName}") - Log.d(TAG, " - ID: ${order.orderId}") - Log.d(TAG, " - Status: '${order.status}'") - Log.d(TAG, " - Customer: '${order.username}'") - Log.d(TAG, " - Total: ${order.totalAmount}") - Log.d(TAG, " - Items count: ${order.orderItems?.size ?: 0}") - Log.d(TAG, " - Created at: ${order.createdAt}") - Log.d(TAG, " - Updated at: ${order.updatedAt}") - - // Log order items if available - order.orderItems?.let { items -> - Log.d(TAG, " Order items:") - items.forEachIndexed { itemIndex, item -> - Log.d(TAG, " Item ${itemIndex + 1}: ${item?.productName} (Qty: ${item?.quantity})") + coroutineScope { + val deferreds = allStatuses.map { status -> + async { + when (val result = repository.getSellList(status)) { + is Result.Success -> { + result.data.orders.onEach { it.displayStatus = status } } + + is Result.Error -> { + Log.e( + TAG, + "Error loading orders for status $status", + result.exception + ) + emptyList() + } + + is Result.Loading -> emptyList() } } } - // Set the ViewState to Success - _sells.value = ViewState.Success(orders) - Log.d(TAG, "✅ ViewState.Success set with ${orders.size} orders") + deferreds.awaitAll().forEach { orders -> + allSells.addAll(orders) + } } - is Result.Error -> { - val endTime = System.currentTimeMillis() - Log.e(TAG, "Repository call failed in ${endTime - startTime}ms") - Log.e(TAG, "❌ Result.Error received from repository") - Log.e(TAG, "Error message: ${result.exception.message}") - Log.e(TAG, "Exception type: ${result.exception.javaClass.simpleName}") - Log.e(TAG, "Exception stack trace:", result.exception) - - val errorMessage = result.exception.message ?: "Unknown error occurred" - _sells.value = ViewState.Error(errorMessage) - Log.d(TAG, "ViewState.Error set with message: '$errorMessage'") + val sortedSells = allSells.sortedByDescending { order -> + try { + SimpleDateFormat( + "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", + Locale.getDefault() + ).parse(order.createdAt) + } catch (e: Exception) { + null + } } - is Result.Loading -> { - Log.d(TAG, "Result.Loading received from repository (this is unusual)") - // Keep the current loading state + _sells.value = ViewState.Success(sortedSells) + Log.d(TAG, "All orders loaded successfully: ${sortedSells.size} items") + } else { + Log.d(TAG, "Calling repository.getSellList(status='$status')") + val startTime = System.currentTimeMillis() + + when (val result = repository.getSellList(status)) { + is Result.Success -> { + val endTime = System.currentTimeMillis() + Log.d(TAG, "Repository call completed in ${endTime - startTime}ms") + Log.d(TAG, "Result.Success received from repository") + + // Log the entire result data structure + Log.d(TAG, "Raw result data: ${result.data}") + Log.d(TAG, "Result data class: ${result.data.javaClass.simpleName}") + + val orders = result.data.orders + Log.d(TAG, "Extracted orders list: $orders") + Log.d(TAG, "Orders list class: ${orders.javaClass.simpleName}") + Log.d(TAG, "Orders count: ${orders.size}") + + // Check if orders list is null or empty + if (false) { + Log.w(TAG, "⚠️ Orders list is NULL") + } else if (orders.isEmpty()) { + Log.w(TAG, "⚠️ Orders list is EMPTY") + } else { + Log.d(TAG, "✅ Orders list contains ${orders.size} items") + + // Log individual order details with more comprehensive info + orders.forEachIndexed { index, order -> + Log.d(TAG, "--- Order ${index + 1}/${orders.size} ---") + Log.d(TAG, " Order object: $order") + Log.d(TAG, " Order class: ${order.javaClass.simpleName}") + Log.d(TAG, " - ID: ${order.orderId}") + Log.d(TAG, " - Status: '${order.status}'") + Log.d(TAG, " - Customer: '${order.username}'") + Log.d(TAG, " - Total: ${order.totalAmount}") + Log.d(TAG, " - Items count: ${order.orderItems?.size ?: 0}") + Log.d(TAG, " - Created at: ${order.createdAt}") + Log.d(TAG, " - Updated at: ${order.updatedAt}") + + // Log order items if available + order.orderItems?.let { items -> + Log.d(TAG, " Order items:") + items.forEachIndexed { itemIndex, item -> + Log.d( + TAG, + " Item ${itemIndex + 1}: ${item?.productName} (Qty: ${item?.quantity})" + ) + } + } + } + } + + // Set the ViewState to Success + _sells.value = ViewState.Success(orders) + Log.d(TAG, "✅ ViewState.Success set with ${orders.size} orders") + } + + is Result.Error -> { + val endTime = System.currentTimeMillis() + Log.e(TAG, "Repository call failed in ${endTime - startTime}ms") + Log.e(TAG, "❌ Result.Error received from repository") + Log.e(TAG, "Error message: ${result.exception.message}") + Log.e(TAG, "Exception type: ${result.exception.javaClass.simpleName}") + Log.e(TAG, "Exception stack trace:", result.exception) + + val errorMessage = result.exception.message ?: "Unknown error occurred" + _sells.value = ViewState.Error(errorMessage) + Log.d(TAG, "ViewState.Error set with message: '$errorMessage'") + } + + is Result.Loading -> { + Log.d(TAG, "Result.Loading received from repository (this is unusual)") + // Keep the current loading state + } } } - } catch (e: Exception) { Log.e(TAG, "❌ Exception caught in getSellList") Log.e(TAG, "Exception type: ${e.javaClass.simpleName}") diff --git a/app/src/main/res/drawable/ic_add.png b/app/src/main/res/drawable/ic_add.png new file mode 100644 index 0000000..4acefe2 Binary files /dev/null and b/app/src/main/res/drawable/ic_add.png differ diff --git a/app/src/main/res/drawable/ic_minus.png b/app/src/main/res/drawable/ic_minus.png new file mode 100644 index 0000000..9d7ed0b Binary files /dev/null and b/app/src/main/res/drawable/ic_minus.png differ diff --git a/app/src/main/res/drawable/ic_under_review.png b/app/src/main/res/drawable/ic_under_review.png new file mode 100644 index 0000000..973390b Binary files /dev/null and b/app/src/main/res/drawable/ic_under_review.png differ diff --git a/app/src/main/res/layout/activity_register_store.xml b/app/src/main/res/layout/activity_register_store.xml index 2ac26d7..7f2a7cf 100644 --- a/app/src/main/res/layout/activity_register_store.xml +++ b/app/src/main/res/layout/activity_register_store.xml @@ -6,7 +6,11 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - tools:context=".ui.auth.RegisterStoreActivity"> + tools:context=".ui.profile.mystore.RegisterStoreActivity"> + + + android:textColor="@color/white" + style="@style/headline_large" /> + android:text="@string/buka_toko_desc" + android:textColor="@color/white" /> @@ -46,510 +48,874 @@ android:orientation="vertical" android:padding="16dp"> - - - - - + android:orientation="horizontal"> - + - + - + - + + android:orientation="vertical" + android:layout_marginVertical="24dp"> - + - + - + + + + + + + + + - + android:orientation="vertical" + android:layout_marginBottom="24dp"> - + + - + + + - + android:orientation="vertical" + android:layout_marginBottom="24dp"> - + + - + + + + + + + + + + + + + + + + + + + + + + android:orientation="vertical" + android:layout_marginBottom="24dp"> - + + - + + + + + + + + + + + + + + + + + + + + + + android:orientation="vertical" + android:layout_marginBottom="24dp"> - + + - + + + + + + + + + + + + + + + + + + + + + + android:orientation="vertical" + android:layout_marginBottom="24dp"> - + - + - + - + + + + + + + + android:orientation="vertical" + android:layout_marginBottom="24dp"> - + - + + + + + + + + + + + + android:orientation="vertical" + android:layout_marginBottom="24dp"> - + + + + + + + + + + + + + + android:orientation="vertical" + android:layout_marginBottom="24dp"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + android:text="17. Pilih Titik Lokasi Usaha" + style="@style/body_medium" + android:layout_marginEnd="4dp"/> - + android:layout_weight="1" + android:text="*" + style="@style/body_medium" + android:textColor="@color/red_required" + android:layout_gravity="end"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:text="Daftar" /> - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_review.xml b/app/src/main/res/layout/activity_review.xml new file mode 100644 index 0000000..40b70c0 --- /dev/null +++ b/app/src/main/res/layout/activity_review.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_store_on_review.xml b/app/src/main/res/layout/activity_store_on_review.xml new file mode 100644 index 0000000..bcb2910 --- /dev/null +++ b/app/src/main/res/layout/activity_store_on_review.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_change_price_bottom_sheet.xml b/app/src/main/res/layout/fragment_change_price_bottom_sheet.xml index 9e7b5ba..81252e8 100644 --- a/app/src/main/res/layout/fragment_change_price_bottom_sheet.xml +++ b/app/src/main/res/layout/fragment_change_price_bottom_sheet.xml @@ -1,14 +1,50 @@ - + tools:context=".ui.profile.mystore.product.ChangePriceBottomSheetFragment" + android:orientation="vertical"> - - + + + + android:layout_height="wrap_content" + android:orientation="horizontal" + android:background="@drawable/bg_text_field" + android:layout_marginTop="10dp" + android:layout_marginHorizontal="16dp"> - \ No newline at end of file + + + + + + +