update dialog pop up and fix display picture

This commit is contained in:
shaulascr
2025-08-27 20:24:39 +07:00
parent 66595fcb48
commit 7fc6458f9b
33 changed files with 499 additions and 217 deletions

View File

@ -5,10 +5,9 @@ import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
import com.alya.ecommerce_serang.R
import com.alya.ecommerce_serang.data.api.dto.FcmReq
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
import com.alya.ecommerce_serang.data.repository.Result
@ -16,6 +15,7 @@ import com.alya.ecommerce_serang.data.repository.UserRepository
import com.alya.ecommerce_serang.databinding.ActivityLoginBinding
import com.alya.ecommerce_serang.ui.MainActivity
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
import com.alya.ecommerce_serang.utils.PopUpDialog
import com.alya.ecommerce_serang.utils.SessionManager
import com.alya.ecommerce_serang.utils.viewmodel.LoginViewModel
import com.google.firebase.FirebaseApp
@ -39,9 +39,9 @@ class LoginActivity : AppCompatActivity() {
binding = ActivityLoginBinding.inflate(layoutInflater)
setContentView(binding.root)
WindowCompat.setDecorFitsSystemWindows(window, false)
enableEdgeToEdge()
//
// WindowCompat.setDecorFitsSystemWindows(window, false)
// enableEdgeToEdge()
setupListeners()
observeLoginState()
@ -83,6 +83,11 @@ class LoginActivity : AppCompatActivity() {
retrieveFCMToken()
// sessionManager.saveUserId(response.userId)
PopUpDialog.showConfirmDialog(
context = this,
iconRes = R.drawable.checkmark__1_,
title = "Berhasil Masuk"
)
Toast.makeText(this, "Berhasil masuk", Toast.LENGTH_SHORT).show()
startActivity(Intent(this, MainActivity::class.java))
@ -90,6 +95,11 @@ class LoginActivity : AppCompatActivity() {
}
is com.alya.ecommerce_serang.data.repository.Result.Error -> {
Log.e("LoginActivity", "Login Failed: ${result.exception.message}")
PopUpDialog.showConfirmDialog(
context = this,
iconRes = R.drawable.ic_cancel,
title = "Gagal Masuk"
)
Toast.makeText(this, "Gagal masuk", Toast.LENGTH_LONG).show()
}
is Result.Loading -> {

View File

@ -5,13 +5,14 @@ import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.alya.ecommerce_serang.R
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
import com.alya.ecommerce_serang.data.repository.Result
import com.alya.ecommerce_serang.data.repository.UserRepository
import com.alya.ecommerce_serang.databinding.ActivityResetPassBinding
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
import com.alya.ecommerce_serang.utils.PopUpDialog
import com.alya.ecommerce_serang.utils.viewmodel.LoginViewModel
class ResetPassActivity : AppCompatActivity() {
@ -36,7 +37,7 @@ class ResetPassActivity : AppCompatActivity() {
setupToolbar()
setupUI()
observeResetPassword()
}
private fun setupToolbar(){
@ -98,26 +99,23 @@ class ResetPassActivity : AppCompatActivity() {
private fun handleSuccess(message: String) {
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
// Show success dialog and navigate back to login
AlertDialog.Builder(this)
.setTitle("Berhasil Ubah Password")
.setMessage(message)
.setPositiveButton("OK") { _, _ ->
// Navigate back to login activity
finish()
}
.setCancelable(false)
.show()
PopUpDialog.showConfirmDialog(
context = this,
iconRes = R.drawable.checkmark__1_,
title = "Berhasil Ubah Password",
positiveText = "OK"
)
}
private fun handleError(errorMessage: String) {
Log.e(TAG, "Error: $errorMessage")
// Optionally show error dialog
AlertDialog.Builder(this)
.setTitle("Gagal Ubah Password")
.setMessage(errorMessage)
.setPositiveButton("OK", null)
.show()
PopUpDialog.showConfirmDialog(
context = this,
iconRes = R.drawable.ic_cancel,
title = "Gagal Ubah Password",
message = errorMessage,
positiveText = "OK"
)
}
}

View File

@ -27,6 +27,7 @@ import com.alya.ecommerce_serang.ui.order.address.SubdsitrictAdapter
import com.alya.ecommerce_serang.ui.order.address.ViewState
import com.alya.ecommerce_serang.ui.order.address.VillagesAdapter
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
import com.alya.ecommerce_serang.utils.PopUpDialog
import com.alya.ecommerce_serang.utils.SessionManager
import com.alya.ecommerce_serang.utils.viewmodel.RegisterViewModel
import com.google.android.material.progressindicator.LinearProgressIndicator
@ -108,7 +109,16 @@ class RegisterStep3Fragment : Fragment() {
}
binding.btnRegister.setOnClickListener {
submitAddress()
PopUpDialog.showConfirmDialog(
context = requireContext(),
title = "Apakah anda yakin data anda sudah benar?",
message = "Pastikan data yang dimasukkan sudah benar",
positiveText = "Ya",
negativeText = "Tidak",
onYesClicked = {
submitAddress()
}
)
}
// Observe address submission state
@ -498,6 +508,7 @@ class RegisterStep3Fragment : Fragment() {
private fun showRegistrationSuccess() {
// Now we can show the success message for the overall registration process
Toast.makeText(requireContext(), "Berhasil mendaftarkan akun", Toast.LENGTH_LONG).show()
sessionManager.clearAll()

View File

@ -43,8 +43,6 @@ class CartActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
sessionManager = SessionManager(this)
apiService = ApiConfig.getApiService(sessionManager)
binding = ActivityCartBinding.inflate(layoutInflater)
setContentView(binding.root)
@ -201,7 +199,8 @@ class CartActivity : AppCompatActivity() {
viewModel.errorMessage.observe(this) { errorMessage ->
errorMessage?.let {
Toast.makeText(this, it, Toast.LENGTH_SHORT).show()
binding.emptyCart.visibility = View.VISIBLE
Log.e("CartActivity", "Error message: $it")
}
}
@ -254,6 +253,10 @@ class CartActivity : AppCompatActivity() {
storeAdapter.updateWholesaleStatus(wholesaleStatusMap, wholesalePriceMap)
}
}
viewModel.productImages.observe(this) { productImages ->
storeAdapter.updateProductImages(productImages)
}
}
private fun showEmptyState(isEmpty: Boolean) {
@ -272,5 +275,5 @@ class CartActivity : AppCompatActivity() {
val format = NumberFormat.getCurrencyInstance(Locale("id", "ID"))
return format.format(amount).replace("Rp", "Rp ")
}
}
}

View File

@ -8,6 +8,7 @@ import androidx.lifecycle.viewModelScope
import com.alya.ecommerce_serang.data.api.dto.UpdateCart
import com.alya.ecommerce_serang.data.api.response.customer.cart.DataItemCart
import com.alya.ecommerce_serang.data.api.response.customer.product.CartItemCheckoutInfo
import com.alya.ecommerce_serang.data.api.response.customer.product.Product
import com.alya.ecommerce_serang.data.repository.OrderRepository
import com.alya.ecommerce_serang.data.repository.Result
import kotlinx.coroutines.launch
@ -52,6 +53,12 @@ class CartViewModel(private val repository: OrderRepository) : ViewModel() {
private val _hasConsistentWholesaleStatus = MutableLiveData<Boolean>(true)
val hasConsistentWholesaleStatus: LiveData<Boolean> = _hasConsistentWholesaleStatus
private val _productDetail = MutableLiveData<Product?>()
val productDetail: LiveData<Product?> get() = _productDetail
private val _productImages = MutableLiveData<Map<Int, String>>()
val productImages: LiveData<Map<Int, String>> = _productImages
fun getCart() {
_isLoading.value = true
_errorMessage.value = null
@ -62,6 +69,12 @@ class CartViewModel(private val repository: OrderRepository) : ViewModel() {
_cartItems.value = result.data
_isLoading.value = false
result.data.forEach { store ->
store.cartItems.forEach { item ->
loadProductImage(item.productId)
}
}
// After loading cart items, check wholesale status
checkWholesaleStatus()
}
@ -404,4 +417,29 @@ class CartViewModel(private val repository: OrderRepository) : ViewModel() {
_hasConsistentWholesaleStatus.value = allSameStatus
}
fun loadProductImage(productId: Int) {
viewModelScope.launch {
try {
val result = repository.fetchProductDetail(productId)
val imageUrl = result?.product?.image ?: ""
val currentMap = _productImages.value?.toMutableMap() ?: mutableMapOf()
currentMap[productId] = imageUrl
_productImages.value = currentMap
} catch (e: Exception) {
Log.e("CartViewModel", "Error loading product image: ${e.message}")
}
}
}
// fun loadProductDetail(productId: Int) {
// viewModelScope.launch {
// val result = repository.fetchProductDetail(productId)
// val currentMap = _productImages.value?.toMutableMap() ?: mutableMapOf()
// currentMap[productId] = result?.product?.image ?: ""
// _productImages.value = currentMap
// }
// }
}

View File

@ -11,6 +11,7 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.alya.ecommerce_serang.BuildConfig.BASE_URL
import com.alya.ecommerce_serang.R
import com.alya.ecommerce_serang.data.api.response.customer.cart.CartItemsItem
import com.alya.ecommerce_serang.data.api.response.customer.cart.DataItemCart
@ -30,6 +31,12 @@ class StoreAdapter(
private var activeStoreId: Int? = null
private var wholesaleStatusMap: Map<Int, Boolean> = mapOf()
private var wholesalePriceMap: Map<Int, Double> = mapOf()
private var productImages: Map<Int, String> = emptyMap()
fun updateProductImages(newImages: Map<Int, String>) {
productImages = newImages
notifyDataSetChanged()
}
companion object {
private const val VIEW_TYPE_STORE = 0
@ -135,7 +142,8 @@ class StoreAdapter(
wholesalePrice,
{ isChecked -> onItemCheckChanged(cartItem.cartItemId, store.storeId, isChecked) },
{ quantity -> onItemQuantityChanged(cartItem.cartItemId, quantity) },
{ onItemDeleted(cartItem.cartItemId) }
{ onItemDeleted(cartItem.cartItemId) },
productImages
)
}
}
@ -197,7 +205,8 @@ class StoreAdapter(
wholesalePrice: Double?,
onCheckedChange: (Boolean) -> Unit,
onQuantityChanged: (Int) -> Unit,
onDelete: () -> Unit
onDelete: () -> Unit,
productImages: Map<Int, String>
) {
// Set product name
tvProductName.text = cartItem.productName
@ -216,20 +225,6 @@ class StoreAdapter(
// Set quantity
tvQuantity.text = cartItem.quantity.toString()
// Visual indication for wholesale items
// if (isWholesale) {
// // You can add a background or border to indicate wholesale items
// // For example:
//// itemView.setBackgroundResource(R.drawable.bg_wholesale_item)
// // If you don't have this drawable, you can use a simple color tint instead:
// itemView.setBackgroundColor(ContextCompat.getColor(itemView.context, R.color.wholesale_item_bg))
// } else {
// // Reset to default background
//// itemView.setBackgroundResource(R.drawable.bg_normal_item)
// // Or if you don't have this drawable:
// itemView.setBackgroundColor(ContextCompat.getColor(itemView.context, R.color.normal_item_bg))
// }
// Set checkbox state without triggering listener
cbItem.setOnCheckedChangeListener(null)
cbItem.isChecked = isSelected
@ -247,11 +242,16 @@ class StoreAdapter(
onCheckedChange(isChecked)
}
// Load product image
val fullImageUrl = when (val img = productImages[cartItem.productId]) {
is String -> {
if (img.startsWith("/")) BASE_URL + img.substring(1) else img
}
else -> null
}
Glide.with(itemView.context)
.load("https://example.com/images/${cartItem.productId}.jpg") // Assume image URL based on product ID
.load(fullImageUrl)
.placeholder(R.drawable.placeholder_image)
.error(R.drawable.placeholder_image)
.into(ivProduct)
// Quantity control

View File

@ -1,9 +1,11 @@
package com.alya.ecommerce_serang.ui.order
import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.alya.ecommerce_serang.BuildConfig.BASE_URL
import com.alya.ecommerce_serang.R
import com.alya.ecommerce_serang.data.api.dto.CheckoutData
import com.alya.ecommerce_serang.data.api.response.customer.cart.CartItemsItem
@ -13,37 +15,57 @@ import com.bumptech.glide.Glide
import java.text.NumberFormat
import java.util.Locale
class CartCheckoutAdapter(private val checkoutData: CheckoutData) :
RecyclerView.Adapter<CartCheckoutAdapter.SellerViewHolder>() {
class CartCheckoutAdapter(
private val checkoutData: CheckoutData
) : RecyclerView.Adapter<CartCheckoutAdapter.SellerViewHolder>() {
class SellerViewHolder(val binding: ItemOrderSellerBinding) : RecyclerView.ViewHolder(binding.root)
private var productImages: Map<Int, String> = emptyMap()
private val viewHolders = mutableListOf<SellerViewHolder>() // Keep references
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SellerViewHolder {
val binding = ItemOrderSellerBinding.inflate(
LayoutInflater.from(parent.context), parent, false
)
return SellerViewHolder(binding)
}
override fun getItemCount(): Int = 1 // Only one seller
override fun onBindViewHolder(holder: SellerViewHolder, position: Int) {
with(holder.binding) {
// Set seller name
tvStoreName.text = checkoutData.sellerName
// Set up products RecyclerView with multiple items
rvSellerOrderProduct.apply {
layoutManager = LinearLayoutManager(context)
adapter = MultiCartItemsAdapter(checkoutData.cartItems)
class SellerViewHolder(val binding: ItemOrderSellerBinding) : RecyclerView.ViewHolder(binding.root) {
val childAdapter = MultiCartItemsAdapter(emptyList(), emptyMap())
init {
binding.rvSellerOrderProduct.apply {
layoutManager = LinearLayoutManager(binding.root.context)
adapter = childAdapter
isNestedScrollingEnabled = false
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SellerViewHolder {
val binding = ItemOrderSellerBinding.inflate(LayoutInflater.from(parent.context), parent, false)
val holder = SellerViewHolder(binding)
viewHolders.add(holder) // Keep reference
return holder
}
fun updateProductImages(newImages: Map<Int, String>) {
productImages = newImages
// Update all existing child adapters
viewHolders.forEach { holder ->
holder.childAdapter.updateProductImages(newImages)
}
}
override fun getItemCount(): Int = 1
override fun onBindViewHolder(holder: SellerViewHolder, position: Int) {
holder.binding.tvStoreName.text = checkoutData.sellerName
holder.childAdapter.updateData(checkoutData.cartItems)
holder.childAdapter.updateProductImages(productImages) // Apply current images
}
override fun onViewRecycled(holder: SellerViewHolder) {
super.onViewRecycled(holder)
viewHolders.remove(holder) // Clean up reference
}
}
class MultiCartItemsAdapter(private val cartItems: List<CartItemsItem>) :
RecyclerView.Adapter<MultiCartItemsAdapter.CartItemViewHolder>() {
class MultiCartItemsAdapter(
private var cartItems: List<CartItemsItem> = emptyList(),
private var productImages: Map<Int, String> = emptyMap()
) : RecyclerView.Adapter<MultiCartItemsAdapter.CartItemViewHolder>() {
class CartItemViewHolder(val binding: ItemOrderProductBinding) : RecyclerView.ViewHolder(binding.root)
@ -56,24 +78,57 @@ class MultiCartItemsAdapter(private val cartItems: List<CartItemsItem>) :
override fun getItemCount(): Int = cartItems.size
fun updateProductImages(images: Map<Int, String>) {
Log.d("MultiCartItemsAdapter", "updateProductImages called with: $images")
Log.d("MultiCartItemsAdapter", "Current cartItems productIds: ${cartItems.map { it.productId }}")
productImages = images
notifyDataSetChanged()
Log.d("MultiCartItemsAdapter", "notifyDataSetChanged() called")
}
override fun onBindViewHolder(holder: CartItemViewHolder, position: Int) {
val item = cartItems[position]
Log.d("MultiCartItemsAdapter", "onBindViewHolder - position: $position, productId: ${item.productId}")
Log.d("MultiCartItemsAdapter", "Available images: $productImages")
with(holder.binding) {
// Set cart item details
tvProductName.text = item.productName
tvProductQuantity.text = "${item.quantity} buah"
tvProductPrice.text = formatCurrency(item.price.toDouble())
// Load placeholder image
val img = productImages[item.productId]
Log.d("MultiCartItemsAdapter", "Image for productId ${item.productId}: $img")
val fullImageUrl = when (img) {
is String -> {
val url = if (img.startsWith("/")) BASE_URL + img.substring(1) else img
Log.d("MultiCartItemsAdapter", "Full image URL: $url")
url
}
else -> {
Log.d("MultiCartItemsAdapter", "No image found, using placeholder")
null
}
}
Log.d("MultiCartItemsAdapter", "Loading image with Glide: $fullImageUrl")
Glide.with(ivProduct.context)
.load(R.drawable.placeholder_image)
.load(fullImageUrl)
.placeholder(R.drawable.placeholder_image)
.error(R.drawable.placeholder_image) // Add error handling
.into(ivProduct)
}
}
// Minimal helpers to update adapter data from parent adapter
fun updateData(items: List<CartItemsItem>) {
cartItems = items
notifyDataSetChanged()
}
private fun formatCurrency(amount: Double): String {
val formatter = NumberFormat.getCurrencyInstance(Locale("in", "ID"))
return formatter.format(amount).replace(",00", "")
}
}
}

View File

@ -36,6 +36,8 @@ class CheckoutActivity : AppCompatActivity() {
private lateinit var binding: ActivityCheckoutBinding
private lateinit var sessionManager: SessionManager
private var paymentAdapter: PaymentMethodAdapter? = null
private var cartCheckoutAdapter: CartCheckoutAdapter? = null
private var checkoutSellerAdapter: CheckoutSellerAdapter? = null
private var paymentMethodsLoaded = false
private val viewModel: CheckoutViewModel by viewModels {
@ -210,6 +212,13 @@ class CheckoutActivity : AppCompatActivity() {
finish()
}
}
viewModel.productImages.observe(this) { images ->
Log.d("CheckoutActivity", "Product images updated: ${images.keys}")
// Update adapter when images arrive
cartCheckoutAdapter?.updateProductImages(images)
checkoutSellerAdapter?.updateProductImages(images)
}
}
private fun setupPaymentMethodsRecyclerView(paymentMethods: List<DetailPaymentItem>) {
@ -271,24 +280,44 @@ class CheckoutActivity : AppCompatActivity() {
}
private fun setupProductRecyclerView(checkoutData: CheckoutData) {
val adapter = if (checkoutData.isBuyNow || checkoutData.cartItems.size <= 1) {
CheckoutSellerAdapter(checkoutData)
if (checkoutData.isBuyNow || checkoutData.cartItems.size <= 1) {
Log.d("CheckoutActivity", "Using CheckoutSellerAdapter")
val adapter = CheckoutSellerAdapter(checkoutData)
// Keep reference for image updates - create a field in your activity
checkoutSellerAdapter = adapter
binding.rvProductItems.apply {
layoutManager = LinearLayoutManager(this@CheckoutActivity)
this.adapter = adapter
isNestedScrollingEnabled = false
}
// Load images for cart items
if (!checkoutData.isBuyNow) {
checkoutData.cartItems.forEach { item ->
viewModel.loadProductImage(item.productId)
}
}
} else {
CartCheckoutAdapter(checkoutData)
}
Log.d("CheckoutActivity", "Using CartCheckoutAdapter")
Log.d("CheckoutActivity", "Cart items count: ${checkoutData.cartItems.size}")
binding.rvProductItems.apply {
layoutManager = LinearLayoutManager(this@CheckoutActivity)
this.adapter = adapter
isNestedScrollingEnabled = false
}
// Create adapter and keep reference
cartCheckoutAdapter = CartCheckoutAdapter(checkoutData)
// if (checkoutData.cartItems.isEmpty()) {
// // Show empty products state
// binding.containerEmptyProducts.visibility = View.VISIBLE
// binding.rvProductItems.visibility = View.GONE
// return
// }
binding.rvProductItems.apply {
layoutManager = LinearLayoutManager(this@CheckoutActivity)
adapter = cartCheckoutAdapter
isNestedScrollingEnabled = false
}
// Load images for each product
checkoutData.cartItems.forEach { item ->
Log.d("CheckoutActivity", "Loading image for productId: ${item.productId}")
viewModel.loadProductImage(item.productId)
}
}
binding.containerEmptyProducts.visibility = View.GONE
binding.rvProductItems.visibility = View.VISIBLE
@ -375,7 +404,7 @@ class CheckoutActivity : AppCompatActivity() {
if (validateOrder()) {
PopUpDialog.showConfirmDialog(
context = this,
title = "Apakah anda yakin inging membuat pesanan?",
title = "Apakah anda yakin membuat pesanan?",
message = "Pastikan data yang dimasukkan sudah benar",
positiveText = "Ya",
negativeText = "Tidak",

View File

@ -11,31 +11,53 @@ import com.alya.ecommerce_serang.databinding.ItemOrderSellerBinding
class CheckoutSellerAdapter(private val checkoutData: CheckoutData) :
RecyclerView.Adapter<CheckoutSellerAdapter.SellerViewHolder>() {
private var productImages: Map<Int, String> = emptyMap()
private var currentViewHolder: SellerViewHolder? = null
class SellerViewHolder(val binding: ItemOrderSellerBinding) : RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SellerViewHolder {
val binding = ItemOrderSellerBinding.inflate(
LayoutInflater.from(parent.context), parent, false
)
return SellerViewHolder(binding)
val holder = SellerViewHolder(binding)
currentViewHolder = holder
return holder
}
override fun getItemCount(): Int = 1 // Only one seller
fun updateProductImages(newImages: Map<Int, String>) {
productImages = newImages
currentViewHolder?.let { holder ->
// Update the nested adapter
val adapter = holder.binding.rvSellerOrderProduct.adapter
when (adapter) {
is SingleCartItemAdapter -> adapter.updateProductImages(newImages)
is SingleProductAdapter -> {
// For SingleProductAdapter, you might need to update differently
// since it uses checkoutData.productImageUrl
}
}
}
}
override fun getItemCount(): Int = 1
override fun onBindViewHolder(holder: SellerViewHolder, position: Int) {
currentViewHolder = holder
with(holder.binding) {
// Set seller name
tvStoreName.text = checkoutData.sellerName
// Set up products RecyclerView
rvSellerOrderProduct.apply {
layoutManager = LinearLayoutManager(context)
adapter = if (checkoutData.isBuyNow) {
// Single product for Buy Now
SingleProductAdapter(checkoutData)
} else {
// Single cart item
SingleCartItemAdapter(checkoutData.cartItems.first())
SingleCartItemAdapter(checkoutData.cartItems.first()).also { adapter ->
// Apply existing images if available
if (productImages.isNotEmpty()) {
adapter.updateProductImages(productImages)
}
}
}
isNestedScrollingEnabled = false
}

View File

@ -40,6 +40,11 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
private val _orderCreated = MutableLiveData<Boolean>()
val orderCreated: LiveData<Boolean> = _orderCreated
private val _productImages = MutableLiveData<Map<Int, String>>(emptyMap())
val productImages: LiveData<Map<Int, String>> = _productImages
private val currentImages = mutableMapOf<Int, String>()
// Initialize "Buy Now" checkout
fun initializeBuyNow(
storeId: Int,
@ -156,9 +161,13 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
)
Log.d(TAG, "CheckoutData initialized with ${matchingItems.size} items")
matchingItems.forEachIndexed { index, item ->
val isWholesale = isWholesaleMap[item.cartItemId] ?: false
Log.d(TAG, "Item $index: ${item.productName}, Price: ${item.price}, IsWholesale: $isWholesale")
matchingItems.forEach { item ->
Log.d("CheckoutViewModel", "About to load image for productId: ${item.productId}")
loadProductImage(item.productId)
}
matchingItems.forEach { item ->
loadProductImage(item.productId)
}
// Calculate totals with updated prices
@ -179,6 +188,8 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
}
}
fun getPaymentMethods() {
viewModelScope.launch {
try {
@ -417,6 +428,31 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
}
}
fun loadProductImage(productId: Int) {
Log.d("CheckoutViewModel", "loadProductImage called for productId: $productId")
viewModelScope.launch {
try {
Log.d("CheckoutViewModel", "Fetching product detail for productId: $productId")
val productDetail = repository.fetchProductDetail(productId)
Log.d("CheckoutViewModel", "Product detail result: $productDetail")
val imageUrl = productDetail?.product?.image
Log.d("CheckoutViewModel", "Extracted image URL: $imageUrl")
currentImages[productId] = imageUrl.toString()
Log.d("CheckoutViewModel", "Updated currentImages: $currentImages")
_productImages.postValue(currentImages.toMap())
Log.d("CheckoutViewModel", "Posted to _productImages LiveData")
} catch (e: Exception) {
Log.e("CheckoutViewModel", "Error loading image for productId $productId", e)
// fallback if error
currentImages[productId] = ""
_productImages.postValue(currentImages.toMap())
}
}
}
// Get shipping price
private fun getShippingPrice(): Double {
val data = _checkoutData.value ?: return 0.0
@ -431,4 +467,5 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
companion object {
private const val TAG = "CheckoutViewModel"
}
}
}

View File

@ -3,6 +3,7 @@ package com.alya.ecommerce_serang.ui.order
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.alya.ecommerce_serang.BuildConfig.BASE_URL
import com.alya.ecommerce_serang.R
import com.alya.ecommerce_serang.data.api.response.customer.cart.CartItemsItem
import com.alya.ecommerce_serang.databinding.ItemOrderProductBinding
@ -13,6 +14,8 @@ import java.util.Locale
class SingleCartItemAdapter(private val cartItem: CartItemsItem) :
RecyclerView.Adapter<SingleCartItemAdapter.CartItemViewHolder>() {
private var productImages: Map<Int, String> = emptyMap()
class CartItemViewHolder(val binding: ItemOrderProductBinding) : RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CartItemViewHolder {
@ -24,16 +27,28 @@ class SingleCartItemAdapter(private val cartItem: CartItemsItem) :
override fun getItemCount(): Int = 1
fun updateProductImages(newImages: Map<Int, String>) {
productImages = newImages
notifyDataSetChanged()
}
override fun onBindViewHolder(holder: CartItemViewHolder, position: Int) {
with(holder.binding) {
// Set cart item details
tvProductName.text = cartItem.productName
tvProductQuantity.text = "${cartItem.quantity} buah"
tvProductPrice.text = formatCurrency(cartItem.price.toDouble())
// Load placeholder image
// Get the image for this product
val img = productImages[cartItem.productId]
val fullImageUrl = when (img) {
is String -> {
if (img.startsWith("/")) BASE_URL + img.substring(1) else img
}
else -> null
}
Glide.with(ivProduct.context)
.load(R.drawable.placeholder_image)
.load(fullImageUrl)
.placeholder(R.drawable.placeholder_image)
.error(R.drawable.placeholder_image)
.into(ivProduct)

View File

@ -3,6 +3,7 @@ package com.alya.ecommerce_serang.ui.order
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.alya.ecommerce_serang.BuildConfig.BASE_URL
import com.alya.ecommerce_serang.R
import com.alya.ecommerce_serang.data.api.dto.CheckoutData
import com.alya.ecommerce_serang.data.api.dto.OrderRequestBuy
@ -36,9 +37,16 @@ class SingleProductAdapter(private val checkoutData: CheckoutData) :
tvProductPrice.text = formatCurrency(checkoutData.productPrice)
val fullImageUrl = when (val img = checkoutData.productImageUrl) {
is String -> {
if (img.startsWith("/")) BASE_URL + img.substring(1) else img
}
else -> null
}
// Load product image
Glide.with(ivProduct.context)
.load(checkoutData.productImageUrl)
.load(fullImageUrl)
.apply(
RequestOptions()
.placeholder(R.drawable.placeholder_image)

View File

@ -33,6 +33,7 @@ import com.alya.ecommerce_serang.data.repository.OrderRepository
import com.alya.ecommerce_serang.data.repository.Result
import com.alya.ecommerce_serang.databinding.ActivityAddEvidencePaymentBinding
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
import com.alya.ecommerce_serang.utils.PopUpDialog
import com.alya.ecommerce_serang.utils.SessionManager
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
@ -166,6 +167,7 @@ class AddEvidencePaymentActivity : AppCompatActivity() {
// Submit button
binding.btnSubmit.setOnClickListener {
validateAndUpload()
Log.d(TAG, "AddEvidencePaymentActivity onCreate completed")
}
@ -313,12 +315,14 @@ class AddEvidencePaymentActivity : AppCompatActivity() {
return
}
//in case applied metode pembayaran yang lain
// if (binding.spinnerPaymentMethod.selectedItemPosition == 0) {
// Toast.makeText(this, "Silahkan pilih metode pembayaran", Toast.LENGTH_SHORT).show()
// return
// }
binding.etAccountNumber.visibility = View.GONE
//in case applied nomor rekening
// if (binding.etAccountNumber.text.toString().trim().isEmpty()) {
// Toast.makeText(this, "Silahkan isi nomor rekening/HP", Toast.LENGTH_SHORT).show()
// return
@ -330,8 +334,16 @@ class AddEvidencePaymentActivity : AppCompatActivity() {
// }
// All validations passed, proceed with upload
uploadPaymentProof()
PopUpDialog.showConfirmDialog(
context = this,
title = "Apakah bukti yang dikirimkan sudah benar?",
message = "Pastikan bukti yang dikirimkan valid",
positiveText = "Ya",
negativeText = "Tidak",
onYesClicked = {
uploadPaymentProof()
}
)
}
private fun uploadPaymentProof() {

View File

@ -232,7 +232,6 @@ class PaymentActivity : AppCompatActivity() {
else -> emptyList()
}
// Tampilkan instruksi dalam dialog
val dialog = AlertDialog.Builder(this)
.setTitle("Petunjuk Transfer $type")
.setItems(instructions.toTypedArray(), null)

View File

@ -57,6 +57,14 @@ class HistoryActivity : AppCompatActivity() {
}
}
// override fun onDialogConfirmed() {
// // Option 1: refresh activity
// recreate()
//
// // Or Option 2: reload only data
// // viewModel.loadOrders()
// }
private fun setupToolbar() {
setSupportActionBar(binding.toolbar)
supportActionBar?.setDisplayShowTitleEnabled(false)

View File

@ -231,19 +231,21 @@ class HistoryViewModel(private val repository: OrderRepository) : ViewModel() {
}
}
private suspend fun refresh(status: String) {
fun refresh(status: String) {
Log.d(TAG, "⏳ refresh(\"$status\") started")
try {
if (status == "all") {
Log.d(TAG, "🌐 Calling getAllOrdersCombined()")
getAllOrdersCombined() // network → cache
} else {
Log.d(TAG, "🌐 repository.getOrderList(\"$status\")")
repository.getOrderList(status) // network → cache
viewModelScope.launch {
if (status == "all") {
Log.d(TAG, "🌐 Calling getAllOrdersCombined()")
getAllOrdersCombined() // network → cache
} else {
Log.d(TAG, "🌐 repository.getOrderList(\"$status\")")
repository.getOrderList(status) // network → cache
}
Log.d(TAG, "✅ refresh(\"$status\") completed (repository updated)")
// Flow that watches DB/cache will emit automatically
}
Log.d(TAG, "✅ refresh(\"$status\") completed (repository updated)")
// Flow that watches DB/cache will emit automatically
} catch (e: Exception) {
Log.e(TAG, "❌ refresh(\"$status\") failed: ${e.message}", e)
}

View File

@ -28,6 +28,7 @@ import com.alya.ecommerce_serang.ui.order.detail.PaymentActivity
import com.alya.ecommerce_serang.ui.order.history.cancelorder.CancelOrderBottomSheet
import com.alya.ecommerce_serang.ui.order.review.CreateReviewActivity
import com.alya.ecommerce_serang.ui.product.ReviewProductActivity
import com.alya.ecommerce_serang.utils.PopUpDialog
import com.google.android.material.button.MaterialButton
import com.google.android.material.textfield.TextInputLayout
import com.google.gson.Gson
@ -41,7 +42,8 @@ import java.util.TimeZone
class OrderHistoryAdapter(
private val onOrderClickListener: (OrdersItem) -> Unit,
private val viewModel: HistoryViewModel,
private val callbacks: OrderActionCallbacks
private val callbacks: OrderActionCallbacks,
private val listener: OnDialogActionListener
) : RecyclerView.Adapter<OrderHistoryAdapter.OrderViewHolder>() {
interface OrderActionCallbacks {
@ -237,9 +239,18 @@ class OrderHistoryAdapter(
callbacks.onShowLoading(true)
// Call ViewModel
viewModel.confirmOrderCompleted(order.orderId, "completed")
PopUpDialog.showConfirmDialog(
context = itemView.context,
title = "Apakah anda yakin pesanan sudah sampai?",
message = "Pastikan pesanan sudah samapi di alamat tujuan",
positiveText = "Ya",
negativeText = "Tidak",
onYesClicked = {
viewModel.confirmOrderCompleted(order.orderId, "completed")
listener.onDialogConfirmed()
}
)
// viewModel.refreshOrders()
}
}
@ -520,9 +531,19 @@ class OrderHistoryAdapter(
val bottomSheet = CancelOrderBottomSheet(
orderId = orderId,
onOrderCancelled = {
callbacks.onOrderCancelled(orderId.toString(), true, "Order cancelled successfully")
// Show a success message
Toast.makeText(context, "Pesanan berhasil dibatalkan", Toast.LENGTH_SHORT).show()
PopUpDialog.showConfirmDialog(
context = itemView.context,
title = "Apakah anda yakin ingin membatalkan pesanan?",
positiveText = "Ya",
negativeText = "Tidak",
onYesClicked = {
callbacks.onOrderCancelled(orderId.toString(), true, "Order cancelled successfully")
// Show a success message
Toast.makeText(context, "Pesanan berhasil dibatalkan", Toast.LENGTH_SHORT).show()
listener.onDialogConfirmed()
}
)
}
)
@ -602,4 +623,8 @@ class OrderHistoryAdapter(
}
}
}
}
interface OnDialogActionListener {
fun onDialogConfirmed()
}

View File

@ -24,7 +24,7 @@ import com.alya.ecommerce_serang.ui.order.history.detailorder.DetailOrderStatusA
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
import com.alya.ecommerce_serang.utils.SessionManager
class OrderListFragment : Fragment(), OrderHistoryAdapter.OrderActionCallbacks {
class OrderListFragment : Fragment(), OrderHistoryAdapter.OrderActionCallbacks, OnDialogActionListener {
private var _binding: FragmentOrderListBinding? = null
private val binding get() = _binding!!
@ -93,7 +93,8 @@ class OrderListFragment : Fragment(), OrderHistoryAdapter.OrderActionCallbacks {
navigateToOrderDetail(order)
},
viewModel = viewModel,
callbacks = this // Pass this fragment as callback
callbacks = this,
listener = this// Pass this fragment as callback
)
orderAdapter.setFragmentStatus(status)
@ -175,7 +176,6 @@ class OrderListFragment : Fragment(), OrderHistoryAdapter.OrderActionCallbacks {
override fun onOrderCancelled(orderId: String, success: Boolean, message: String) {
if (success) {
Toast.makeText(requireContext(), "Berhasil batalkan pesanan", Toast.LENGTH_SHORT).show()
Log.d("OrderListFragment", "Order cancel success: $message")
// loadOrders() // Refresh the list
if (success) viewModel.updateStatus(status, forceRefresh = true)
@ -210,4 +210,14 @@ class OrderListFragment : Fragment(), OrderHistoryAdapter.OrderActionCallbacks {
super.onResume()
observeOrderList()
}
override fun onDialogConfirmed() {
viewModel.refresh(status)
// Option 1: refresh seluruh fragment
requireActivity().supportFragmentManager.beginTransaction()
.detach(this)
.attach(this)
.commit()
}
}

View File

@ -130,13 +130,6 @@ class CancelOrderBottomSheet(
is Result.Success -> {
// Hide loading indicator
showLoading(false)
// Show success message
Toast.makeText(
context,
"Pesanan berhasil dibatalkan",
Toast.LENGTH_SHORT
).show()
Log.d(TAG, "Cancel order status: SUCCESS, message: ${result.data.message}")
// Notify callback and close dialog

View File

@ -91,10 +91,11 @@ class CategoryProductsActivity : AppCompatActivity() {
// title = category.name
}
val fullImageUrl = if (category.image.startsWith("/")) {
BASE_URL + category.image.removePrefix("/") // Append base URL if the path starts with "/"
} else {
category.image // Use as is if it's already a full URL
val fullImageUrl = when (val img = category.image) {
is String -> {
if (img.startsWith("/")) BASE_URL + img.substring(1) else img
}
else -> null
}
// Load category image

View File

@ -1,6 +1,5 @@
package com.alya.ecommerce_serang.ui.profile
import android.app.AlertDialog
import android.app.ProgressDialog
import android.content.Intent
import android.os.Bundle
@ -27,6 +26,7 @@ import com.alya.ecommerce_serang.ui.profile.mystore.RegisterStoreActivity
import com.alya.ecommerce_serang.ui.profile.mystore.StoreOnReviewActivity
import com.alya.ecommerce_serang.ui.profile.mystore.StoreSuspendedActivity
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
import com.alya.ecommerce_serang.utils.PopUpDialog
import com.alya.ecommerce_serang.utils.SessionManager
import com.alya.ecommerce_serang.utils.viewmodel.MyStoreViewModel
import com.alya.ecommerce_serang.utils.viewmodel.ProfileViewModel
@ -200,14 +200,16 @@ class ProfileFragment : Fragment() {
private fun logout(){
AlertDialog.Builder(requireContext())
.setTitle("Konfirmasi")
.setMessage("Apakah anda yakin ingin keluar?")
.setPositiveButton("Ya") { _, _ ->
PopUpDialog.showConfirmDialog(
context = requireContext(),
title = "Konfirmasi",
message = "Apakah anda yakin ingin keluar?",
positiveText = "Ya",
negativeText = "Tidak",
onYesClicked = {
actionLogout()
}
.setNegativeButton("Tidak", null)
.show()
)
}
private fun actionLogout(){

View File

@ -2,7 +2,6 @@ package com.alya.ecommerce_serang.ui.profile.editprofile
import android.Manifest
import android.app.Activity
import android.app.AlertDialog
import android.app.DatePickerDialog
import android.content.Intent
import android.content.pm.PackageManager
@ -29,6 +28,7 @@ import com.alya.ecommerce_serang.data.repository.Result
import com.alya.ecommerce_serang.data.repository.UserRepository
import com.alya.ecommerce_serang.databinding.ActivityEditProfileCustBinding
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
import com.alya.ecommerce_serang.utils.PopUpDialog
import com.alya.ecommerce_serang.utils.SessionManager
import com.alya.ecommerce_serang.utils.viewmodel.ProfileViewModel
import com.bumptech.glide.Glide
@ -213,12 +213,17 @@ class EditProfileCustActivity : AppCompatActivity() {
}
private fun confirmUpdate() {
AlertDialog.Builder(this)
.setTitle("Konfirmasi Perubahan")
.setMessage("Apakah Anda yakin ingin menyimpan perubahan profil toko Anda?")
.setPositiveButton("Ya") { _, _ -> saveProfile() }
.setNegativeButton("Batal", null)
.show()
PopUpDialog.showConfirmDialog(
context = this,
title = "Apakah Anda yakin ingin menyimpan perubahan profil?",
message = "Pastikan data yang dimasukkan sudah benar",
positiveText = "Ya",
negativeText = "Tidak",
onYesClicked = {
saveProfile()
}
)
}
private fun hasChanged(): Boolean {

View File

@ -24,6 +24,7 @@ import com.alya.ecommerce_serang.R
import com.alya.ecommerce_serang.data.api.response.store.profile.Payment
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
import com.alya.ecommerce_serang.utils.ImageUtils.compressImage
import com.alya.ecommerce_serang.utils.PopUpDialog
import com.alya.ecommerce_serang.utils.SessionManager
import kotlinx.coroutines.launch
import okhttp3.MediaType.Companion.toMediaTypeOrNull
@ -374,14 +375,11 @@ class BalanceTopUpActivity : AppCompatActivity() {
// Show a dialog with the success message
runOnUiThread {
AlertDialog.Builder(this@BalanceTopUpActivity)
.setTitle("Berhasil")
.setMessage(successMessage)
.setPositiveButton("OK") { dialog, _ ->
dialog.dismiss()
finish()
}
.show()
PopUpDialog.showConfirmDialog(
context = this@BalanceTopUpActivity,
iconRes = R.drawable.checkmark__1_,
title = "Berhasil melakukan Top-Up"
)
}
} else {
// Get more detailed error information
@ -408,13 +406,12 @@ class BalanceTopUpActivity : AppCompatActivity() {
// Show a dialog with the error message
runOnUiThread {
AlertDialog.Builder(this@BalanceTopUpActivity)
.setTitle("Error Response")
.setMessage(errorMessage)
.setPositiveButton("OK") { dialog, _ ->
dialog.dismiss()
}
.show()
PopUpDialog.showConfirmDialog(
context = this@BalanceTopUpActivity,
iconRes = R.drawable.ic_cancel,
title = "Gagal melakukan Top-Up"
)
}
}
} catch (e: Exception) {

View File

@ -29,9 +29,10 @@ 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
import com.alya.ecommerce_serang.utils.viewmodel.MyStoreViewModel
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
import com.alya.ecommerce_serang.utils.PopUpDialog
import com.alya.ecommerce_serang.utils.SessionManager
import com.alya.ecommerce_serang.utils.viewmodel.MyStoreViewModel
import com.bumptech.glide.Glide
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
@ -39,7 +40,6 @@ 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
@ -244,12 +244,17 @@ class DetailStoreProfileActivity : AppCompatActivity() {
}
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()
PopUpDialog.showConfirmDialog(
context = this,
title = "Apakah Anda yakin ingin menyimpan perubahan profil?",
message = "Pastikan data yang dimasukkan sudah benar",
positiveText = "Ya",
negativeText = "Tidak",
onYesClicked = {
updateStoreProfile()
}
)
}
private fun showImageOptions() {

View File

@ -142,7 +142,7 @@ class SellsListFragment : Fragment() {
binding.progressBar.visibility = View.GONE
binding.tvEmptyState.visibility = View.VISIBLE
Toast.makeText(requireContext(), result.message, Toast.LENGTH_SHORT).show()
// Toast.makeText(requireContext(), result.message, Toast.LENGTH_SHORT).show()
}
is ViewState.Loading -> {
binding.progressBar.visibility = View.VISIBLE

View File

@ -1,22 +0,0 @@
package com.alya.ecommerce_serang.utils
import android.content.Context
import android.graphics.Rect
import android.view.View
import androidx.annotation.DimenRes
import androidx.recyclerview.widget.RecyclerView
class HorizontalMarginItemDecoration(context: Context, @DimenRes horizontalMarginInDp: Int) :
RecyclerView.ItemDecoration() {
private val horizontalMarginInPx: Int =
context.resources.getDimension(horizontalMarginInDp).toInt()
override fun getItemOffsets(
outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State
) {
outRect.right = horizontalMarginInPx
outRect.left = horizontalMarginInPx
}
}

View File

@ -78,7 +78,6 @@ object PopUpDialog {
dialog.dismiss()
}
dialog.show()
}
}

View File

@ -4,8 +4,8 @@
<!-- Logo with controlled size -->
<item
android:width="200dp"
android:height="200dp"
android:width="120dp"
android:height="120dp"
android:drawable="@drawable/logo_psb_crop"
android:gravity="center" />
</layer-list>

View File

@ -12,11 +12,13 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:orientation="vertical">
<ImageView
android:id="@+id/dialogIcon"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginTop="16dp"
android:layout_gravity="center_horizontal"
android:contentDescription="icon dialog"
android:visibility="gone" />
@ -27,8 +29,14 @@
android:layout_height="wrap_content"
android:text="Judul"
android:layout_marginTop="16dp"
android:paddingHorizontal="16dp"
android:paddingTop="4dp"
android:ellipsize="end"
android:scrollHorizontally="false"
android:singleLine="false"
android:gravity="center"
android:textSize="18sp"
android:textSize="16sp"
android:maxLines="3"
android:fontFamily="@font/dmsans_semibold"
android:textColor="?attr/colorOnSurface" />
@ -38,9 +46,15 @@
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:gravity="center"
android:maxLines="3"
android:paddingHorizontal="16dp"
android:paddingTop="4dp"
android:ellipsize="end"
android:scrollHorizontally="false"
android:singleLine="false"
android:text="Pesan Dialog"
android:fontFamily="@font/dmsans_regular"
android:textSize="14sp"
android:textSize="12sp"
android:textColor="?attr/colorOnSurfaceVariant" />
<LinearLayout
@ -49,6 +63,7 @@
android:layout_marginTop="20dp"
android:gravity="center"
android:layout_marginBottom="16dp"
android:paddingHorizontal="16dp"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
@ -57,6 +72,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/dmsans_regular"
android:theme="@style/body_small"
android:text="Tidak" />
<com.google.android.material.button.MaterialButton
@ -66,6 +83,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:fontFamily="@font/dmsans_regular"
android:theme="@style/body_small"
android:text="Ya" />
</LinearLayout>

View File

@ -25,7 +25,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="Gracia Hotmauli"
android:text="Nama Pengguna"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="@id/profileImage"
@ -35,7 +35,7 @@
android:id="@+id/tvUsername"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="\@gracia34"
android:text="Username"
android:textColor="#757575"
app:layout_constraintStart_toStartOf="@id/tvName"
app:layout_constraintTop_toBottomOf="@id/tvName" />

View File

@ -3,8 +3,6 @@
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
card_view:cardCornerRadius="12dp"
card_view:cardElevation="6dp"
android:foreground="?android:attr/selectableItemBackground">
@ -18,17 +16,10 @@
android:id="@+id/tvOption"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/dmsans_semibold"
android:fontFamily="@font/dmsans_regular"
android:text="Item 1"
android:textColor="@color/black_500"
android:textSize="16sp" />
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@android:color/darker_gray"
android:layout_marginTop="4dp" />
</LinearLayout>
</androidx.cardview.widget.CardView>

View File

@ -133,21 +133,33 @@
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_left"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_button_outline"
android:backgroundTint="@color/white"
android:visibility="gone"
android:maxLines="2"
android:ellipsize="end"
android:scrollHorizontally="false"
android:singleLine="false"
android:padding="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvDeadlineDate"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="8dp"/>
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_right"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_button_filled"
android:visibility="gone"
android:maxLines="2"
android:ellipsize="end"
android:scrollHorizontally="false"
android:singleLine="false"
android:padding="4dp"
app:layout_constraintBottom_toBottomOf="parent"
android:text="Kirim Bukti Bayar"
app:layout_constraintTop_toBottomOf="@id/tvDeadlineDate"

View File

@ -338,7 +338,6 @@
<style name="ThemeOverlay.MyApp.AlertDialog" parent="ThemeOverlay.Material3.MaterialAlertDialog">
<!-- Rounded corners -->
<item name="shapeAppearanceMediumComponent">@style/ShapeAppearance.MyApp.MediumComponent</item>
<!-- Dialog background color -->
<item name="cardBackgroundColor">@color/white</item>
<item name="materialAlertDialogBodyTextStyle">@font/dmsans_regular</item>
<item name="android:windowBackground">@android:color/transparent</item>