mirror of
https://github.com/shaulascr/ecommerce_serang.git
synced 2025-08-09 08:52:21 +00:00
Merge branch 'screen-features'
This commit is contained in:
@ -80,7 +80,7 @@ data class Orders(
|
||||
val cancelReason: String? = null,
|
||||
|
||||
@field:SerializedName("total_amount")
|
||||
val totalAmount: String? = null,
|
||||
val totalAmount: Int? = null,
|
||||
|
||||
@field:SerializedName("user_id")
|
||||
val userId: Int,
|
||||
|
@ -394,7 +394,10 @@ class ChatActivity : AppCompatActivity() {
|
||||
|
||||
// Update messages
|
||||
val previousCount = chatAdapter.itemCount
|
||||
chatAdapter.submitList(state.messages) {
|
||||
|
||||
val displayItems = viewModel.getDisplayItems()
|
||||
|
||||
chatAdapter.submitList(displayItems) {
|
||||
Log.d(TAG, "Messages submitted to adapter")
|
||||
// Only auto-scroll for new messages or initial load
|
||||
if (previousCount == 0 || state.messages.size > previousCount) {
|
||||
@ -426,17 +429,15 @@ class ChatActivity : AppCompatActivity() {
|
||||
.error(R.drawable.placeholder_image)
|
||||
.into(binding.imgProduct)
|
||||
}
|
||||
updateProductCardUI(state.hasProductAttachment)
|
||||
|
||||
binding.productContainer.visibility = View.VISIBLE
|
||||
binding.productContainer.visibility = View.GONE
|
||||
} else {
|
||||
binding.productContainer.visibility = View.GONE
|
||||
}
|
||||
|
||||
updateInputHint(state)
|
||||
|
||||
// Update product card visual feedback
|
||||
updateProductCardUI(state.hasProductAttachment)
|
||||
|
||||
// Update attachment hint
|
||||
if (state.hasAttachment) {
|
||||
binding.editTextMessage.hint = getString(R.string.image_attached)
|
||||
|
@ -8,6 +8,7 @@ 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.databinding.ItemDateHeaderBinding
|
||||
import com.alya.ecommerce_serang.databinding.ItemMessageProductReceivedBinding
|
||||
import com.alya.ecommerce_serang.databinding.ItemMessageProductSentBinding
|
||||
import com.alya.ecommerce_serang.databinding.ItemMessageReceivedBinding
|
||||
@ -17,22 +18,29 @@ import com.bumptech.glide.Glide
|
||||
|
||||
class ChatAdapter(
|
||||
private val onProductClick: ((ProductInfo) -> Unit)? = null
|
||||
) : ListAdapter<ChatUiMessage, RecyclerView.ViewHolder>(ChatMessageDiffCallback()) {
|
||||
) : ListAdapter<ChatDisplayItem, RecyclerView.ViewHolder>(ChatMessageDiffCallback()) {
|
||||
|
||||
companion object {
|
||||
private const val VIEW_TYPE_MESSAGE_SENT = 1
|
||||
private const val VIEW_TYPE_MESSAGE_RECEIVED = 2
|
||||
private const val VIEW_TYPE_PRODUCT_SENT = 3
|
||||
private const val VIEW_TYPE_PRODUCT_RECEIVED = 4
|
||||
private const val VIEW_TYPE_DATE_HEADER = 5
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
val message = getItem(position)
|
||||
return when {
|
||||
message.messageType == MessageType.PRODUCT && message.isSentByMe -> VIEW_TYPE_PRODUCT_SENT
|
||||
message.messageType == MessageType.PRODUCT && !message.isSentByMe -> VIEW_TYPE_PRODUCT_RECEIVED
|
||||
message.isSentByMe -> VIEW_TYPE_MESSAGE_SENT
|
||||
else -> VIEW_TYPE_MESSAGE_RECEIVED
|
||||
val item = getItem(position)
|
||||
return when (item) {
|
||||
is ChatDisplayItem.DateHeaderItem -> VIEW_TYPE_DATE_HEADER
|
||||
is ChatDisplayItem.MessageItem -> {
|
||||
val message = item.chatUiMessage
|
||||
when {
|
||||
message.messageType == MessageType.PRODUCT && message.isSentByMe -> VIEW_TYPE_PRODUCT_SENT
|
||||
message.messageType == MessageType.PRODUCT && !message.isSentByMe -> VIEW_TYPE_PRODUCT_RECEIVED
|
||||
message.isSentByMe -> VIEW_TYPE_MESSAGE_SENT
|
||||
else -> VIEW_TYPE_MESSAGE_RECEIVED
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,6 +48,10 @@ class ChatAdapter(
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
|
||||
return when (viewType) {
|
||||
VIEW_TYPE_DATE_HEADER -> {
|
||||
val binding = ItemDateHeaderBinding.inflate(inflater, parent, false)
|
||||
DateHeaderViewHolder(binding)
|
||||
}
|
||||
VIEW_TYPE_MESSAGE_SENT -> {
|
||||
val binding = ItemMessageSentBinding.inflate(inflater, parent, false)
|
||||
SentMessageViewHolder(binding)
|
||||
@ -61,13 +73,34 @@ class ChatAdapter(
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
val message = getItem(position)
|
||||
val item = getItem(position)
|
||||
|
||||
when (holder) {
|
||||
is SentMessageViewHolder -> holder.bind(message)
|
||||
is ReceivedMessageViewHolder -> holder.bind(message)
|
||||
is SentProductViewHolder -> holder.bind(message)
|
||||
is ReceivedProductViewHolder -> holder.bind(message)
|
||||
is DateHeaderViewHolder -> {
|
||||
if (item is ChatDisplayItem.DateHeaderItem) {
|
||||
holder.bind(item)
|
||||
}
|
||||
}
|
||||
is SentMessageViewHolder -> {
|
||||
if (item is ChatDisplayItem.MessageItem) {
|
||||
holder.bind(item.chatUiMessage)
|
||||
}
|
||||
}
|
||||
is ReceivedMessageViewHolder -> {
|
||||
if (item is ChatDisplayItem.MessageItem) {
|
||||
holder.bind(item.chatUiMessage)
|
||||
}
|
||||
}
|
||||
is SentProductViewHolder -> {
|
||||
if (item is ChatDisplayItem.MessageItem) {
|
||||
holder.bind(item.chatUiMessage)
|
||||
}
|
||||
}
|
||||
is ReceivedProductViewHolder -> {
|
||||
if (item is ChatDisplayItem.MessageItem) {
|
||||
holder.bind(item.chatUiMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -233,17 +266,36 @@ class ChatAdapter(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inner class DateHeaderViewHolder(private val binding: ItemDateHeaderBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(item: ChatDisplayItem.DateHeaderItem) {
|
||||
binding.tvDate.text = item.formattedDate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DiffUtil callback for optimizing RecyclerView updates
|
||||
*/
|
||||
class ChatMessageDiffCallback : DiffUtil.ItemCallback<ChatUiMessage>() {
|
||||
override fun areItemsTheSame(oldItem: ChatUiMessage, newItem: ChatUiMessage): Boolean {
|
||||
return oldItem.id == newItem.id
|
||||
class ChatMessageDiffCallback : DiffUtil.ItemCallback<ChatDisplayItem>() {
|
||||
override fun areItemsTheSame(oldItem: ChatDisplayItem, newItem: ChatDisplayItem): Boolean {
|
||||
return when {
|
||||
oldItem is ChatDisplayItem.MessageItem && newItem is ChatDisplayItem.MessageItem ->
|
||||
oldItem.chatUiMessage.id == newItem.chatUiMessage.id
|
||||
oldItem is ChatDisplayItem.DateHeaderItem && newItem is ChatDisplayItem.DateHeaderItem ->
|
||||
oldItem.date == newItem.date
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: ChatUiMessage, newItem: ChatUiMessage): Boolean {
|
||||
override fun areContentsTheSame(oldItem: ChatDisplayItem, newItem: ChatDisplayItem): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ChatDisplayItem {
|
||||
data class MessageItem(val chatUiMessage: ChatUiMessage) : ChatDisplayItem()
|
||||
data class DateHeaderItem(val date: String, val formattedDate: String) : ChatDisplayItem()
|
||||
}
|
@ -16,6 +16,9 @@ import com.alya.ecommerce_serang.utils.SessionManager
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import java.util.TimeZone
|
||||
import javax.inject.Inject
|
||||
@ -737,7 +740,8 @@ class ChatViewModel @Inject constructor(
|
||||
attachment = chatLine.attachment ?: "",
|
||||
status = chatLine.status,
|
||||
time = formattedTime,
|
||||
isSentByMe = chatLine.senderId == currentUserId
|
||||
isSentByMe = chatLine.senderId == currentUserId,
|
||||
createdAt = chatLine.createdAt
|
||||
)
|
||||
}
|
||||
|
||||
@ -781,7 +785,8 @@ class ChatViewModel @Inject constructor(
|
||||
time = formattedTime,
|
||||
isSentByMe = chatItem.senderId == currentUserId,
|
||||
messageType = messageType,
|
||||
productInfo = productInfo
|
||||
productInfo = productInfo,
|
||||
createdAt = chatItem.createdAt
|
||||
)
|
||||
|
||||
// Fetch product info for non-current-user products
|
||||
@ -923,6 +928,85 @@ class ChatViewModel @Inject constructor(
|
||||
Log.d(TAG, "ChatViewModel cleared - Disconnecting socket")
|
||||
socketService.disconnect()
|
||||
}
|
||||
|
||||
fun getDisplayItems(): List<ChatDisplayItem> {
|
||||
return transformMessagesToDisplayItems(state.value?.messages ?: emptyList())
|
||||
}
|
||||
|
||||
private fun transformMessagesToDisplayItems(messages: List<ChatUiMessage>): List<ChatDisplayItem> {
|
||||
if (messages.isEmpty()) return emptyList()
|
||||
|
||||
val displayItems = mutableListOf<ChatDisplayItem>()
|
||||
var lastDate: String? = null
|
||||
|
||||
for (message in messages) {
|
||||
// Extract date from message timestamp
|
||||
val messageDate = extractDateFromTimestamp(message.createdAt) // You need to implement this
|
||||
|
||||
// Add date header if this is a new day
|
||||
if (messageDate != lastDate) {
|
||||
val formattedDate = formatDateHeader(messageDate) // You need to implement this
|
||||
displayItems.add(ChatDisplayItem.DateHeaderItem(messageDate, formattedDate))
|
||||
lastDate = messageDate
|
||||
}
|
||||
|
||||
// Add the message
|
||||
displayItems.add(ChatDisplayItem.MessageItem(message))
|
||||
}
|
||||
|
||||
return displayItems
|
||||
}
|
||||
|
||||
private fun extractDateFromTimestamp(timestamp: String): String {
|
||||
return try {
|
||||
// Parse ISO 8601 format: "2025-05-27T08:36:53.946Z"
|
||||
val inputFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault())
|
||||
inputFormat.timeZone = TimeZone.getTimeZone("UTC")
|
||||
|
||||
val date = inputFormat.parse(timestamp)
|
||||
val outputFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
|
||||
outputFormat.format(date ?: Date())
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error parsing timestamp: $timestamp", e)
|
||||
return timestamp.take(10)
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatDateHeader(dateString: String): String {
|
||||
return try {
|
||||
val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
|
||||
val messageDate = dateFormat.parse(dateString) ?: return dateString
|
||||
|
||||
val today = Calendar.getInstance()
|
||||
val yesterday = Calendar.getInstance().apply { add(Calendar.DAY_OF_YEAR, -1) }
|
||||
val messageCalendar = Calendar.getInstance().apply { time = messageDate }
|
||||
|
||||
when {
|
||||
isSameDay(messageCalendar, today) -> "Today"
|
||||
isSameDay(messageCalendar, yesterday) -> "Yesterday"
|
||||
isThisYear(messageCalendar, today) -> {
|
||||
// Show "Mon, Dec 15" format for this year
|
||||
SimpleDateFormat("EEE, MMM dd", Locale.getDefault()).format(messageDate)
|
||||
}
|
||||
else -> {
|
||||
// Show "Dec 15, 2024" format for other years
|
||||
SimpleDateFormat("MMM dd, yyyy", Locale.getDefault()).format(messageDate)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error formatting date: $dateString", e)
|
||||
dateString // Fallback to raw date
|
||||
}
|
||||
}
|
||||
|
||||
private fun isSameDay(cal1: Calendar, cal2: Calendar): Boolean {
|
||||
return cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
|
||||
cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR)
|
||||
}
|
||||
|
||||
private fun isThisYear(messageCalendar: Calendar, today: Calendar): Boolean {
|
||||
return messageCalendar.get(Calendar.YEAR) == today.get(Calendar.YEAR)
|
||||
}
|
||||
}
|
||||
|
||||
enum class MessageType {
|
||||
@ -949,9 +1033,12 @@ data class ChatUiMessage(
|
||||
val time: String,
|
||||
val isSentByMe: Boolean,
|
||||
val messageType: MessageType = MessageType.TEXT,
|
||||
val productInfo: ProductInfo? = null
|
||||
val productInfo: ProductInfo? = null,
|
||||
val createdAt: String
|
||||
)
|
||||
|
||||
|
||||
|
||||
// representing UI state to screen
|
||||
data class ChatUiState(
|
||||
val messages: List<ChatUiMessage> = emptyList(),
|
||||
|
@ -6,7 +6,6 @@ import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
@ -110,26 +109,16 @@ class HomeFragment : Fragment() {
|
||||
|
||||
private fun setupSearchView() {
|
||||
binding.searchContainer.search.apply {
|
||||
// When user clicks the search box, navigate to search fragment
|
||||
// Make it non-editable so it acts like a button
|
||||
isFocusable = false
|
||||
isFocusableInTouchMode = false
|
||||
isClickable = true
|
||||
|
||||
setOnClickListener {
|
||||
findNavController().navigate(
|
||||
HomeFragmentDirections.actionHomeFragmentToSearchHomeFragment(null)
|
||||
)
|
||||
}
|
||||
|
||||
// Handle search action if user presses search on keyboard
|
||||
setOnEditorActionListener { _, actionId, _ ->
|
||||
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
|
||||
val query = text.toString().trim()
|
||||
if (query.isNotEmpty()) {
|
||||
findNavController().navigate(
|
||||
HomeFragmentDirections.actionHomeFragmentToSearchHomeFragment(query)
|
||||
)
|
||||
}
|
||||
return@setOnEditorActionListener true
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// Setup cart and notification buttons
|
||||
|
@ -2,6 +2,7 @@ package com.alya.ecommerce_serang.ui.home
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
@ -11,6 +12,8 @@ import com.alya.ecommerce_serang.data.api.dto.ProductsItem
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.product.StoreItem
|
||||
import com.alya.ecommerce_serang.databinding.ItemProductGridBinding
|
||||
import com.bumptech.glide.Glide
|
||||
import java.text.NumberFormat
|
||||
import java.util.Locale
|
||||
|
||||
class HorizontalProductAdapter(
|
||||
private var products: List<ProductsItem>,
|
||||
@ -32,8 +35,17 @@ class HorizontalProductAdapter(
|
||||
Log.d("ProductAdapter", "Loading image: $fullImageUrl")
|
||||
|
||||
tvProductName.text = product.name
|
||||
tvProductPrice.text = product.price
|
||||
rating.text = product.rating
|
||||
tvProductPrice.text = formatCurrency(product.price.toDouble())
|
||||
val ratingStr = product.rating
|
||||
val ratingValue = ratingStr?.toFloatOrNull()
|
||||
|
||||
if (ratingValue != null && ratingValue > 0f) {
|
||||
binding.rating.text = String.format("%.1f", ratingValue)
|
||||
binding.rating.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.rating.text = "Belum ada rating"
|
||||
binding.rating.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null)
|
||||
}
|
||||
|
||||
// Load image using Glide
|
||||
Glide.with(itemView)
|
||||
@ -77,6 +89,11 @@ class HorizontalProductAdapter(
|
||||
diffResult.dispatchUpdatesTo(this)
|
||||
}
|
||||
|
||||
private fun formatCurrency(amount: Double): String {
|
||||
val formatter = NumberFormat.getCurrencyInstance(Locale("in", "ID"))
|
||||
return formatter.format(amount).replace(",00", "")
|
||||
}
|
||||
|
||||
class ProductDiffCallback(
|
||||
private val oldList: List<ProductsItem>,
|
||||
private val newList: List<ProductsItem>
|
||||
|
@ -76,6 +76,7 @@ class SearchHomeFragment : Fragment() {
|
||||
|
||||
// Setup search view
|
||||
binding.searchView.apply {
|
||||
clearFocus()
|
||||
setOnQueryTextListener(object : androidx.appcompat.widget.SearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String?): Boolean {
|
||||
query?.let {
|
||||
@ -105,13 +106,12 @@ class SearchHomeFragment : Fragment() {
|
||||
}
|
||||
})
|
||||
|
||||
// Request focus and show keyboard
|
||||
if (args.query.isNullOrEmpty()) {
|
||||
requestFocus()
|
||||
postDelayed({
|
||||
post {
|
||||
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
imm.showSoftInput(findFocus(), InputMethodManager.SHOW_IMPLICIT)
|
||||
}, 200)
|
||||
imm.showSoftInput(this, InputMethodManager.SHOW_IMPLICIT)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ import com.alya.ecommerce_serang.data.api.dto.ProductsItem
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.product.StoreItem
|
||||
import com.alya.ecommerce_serang.databinding.ItemProductGridBinding
|
||||
import com.bumptech.glide.Glide
|
||||
import java.text.NumberFormat
|
||||
import java.util.Locale
|
||||
|
||||
class SearchResultsAdapter(
|
||||
private val onItemClick: (ProductsItem) -> Unit,
|
||||
@ -46,7 +48,7 @@ class SearchResultsAdapter(
|
||||
|
||||
fun bind(product: ProductsItem) {
|
||||
binding.tvProductName.text = product.name
|
||||
binding.tvProductPrice.text = product.price
|
||||
binding.tvProductPrice.text = formatCurrency(product.price.toDouble())
|
||||
|
||||
val fullImageUrl = if (product.image.startsWith("/")) {
|
||||
BASE_URL + product.image.removePrefix("/") // Append base URL if the path starts with "/"
|
||||
@ -71,6 +73,11 @@ class SearchResultsAdapter(
|
||||
super.submitList(list)
|
||||
}
|
||||
|
||||
private fun formatCurrency(amount: Double): String {
|
||||
val formatter = NumberFormat.getCurrencyInstance(Locale("in", "ID"))
|
||||
return formatter.format(amount).replace(",00", "")
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<ProductsItem>() {
|
||||
override fun areItemsTheSame(oldItem: ProductsItem, newItem: ProductsItem): Boolean {
|
||||
|
@ -48,6 +48,12 @@ class PaymentMethodAdapter(
|
||||
|
||||
// Load payment icon if available
|
||||
if (!payment.qrisImage.isNullOrEmpty()) {
|
||||
// val fullImageUrl = if (payment.qrisImage.startsWith("/")) {
|
||||
// BASE_URL + payment.qrisImage.removePrefix("/") // Append base URL if the path starts with "/"
|
||||
// } else {
|
||||
// payment.qrisImage// Use as is if it's already a full URL
|
||||
// }
|
||||
|
||||
Glide.with(ivPaymentMethod.context)
|
||||
.load(payment.qrisImage)
|
||||
.apply(
|
||||
|
@ -6,6 +6,8 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.CourierCostsItem
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.ServicesItem
|
||||
import com.alya.ecommerce_serang.databinding.ItemShippingOrderBinding
|
||||
import java.text.NumberFormat
|
||||
import java.util.Locale
|
||||
|
||||
class ShippingAdapter(
|
||||
private val onItemSelected: (CourierCostsItem, ServicesItem) -> Unit
|
||||
@ -30,7 +32,7 @@ class ShippingAdapter(
|
||||
// Combine courier name and service
|
||||
courierNameCost.text = "${courierCostsItem.courier} - ${service.service}"
|
||||
estDate.text = "Estimasi ${service.etd} hari"
|
||||
costPrice.text = "Rp${service.cost}"
|
||||
costPrice.text = formatCurrency(service.cost.toDouble())
|
||||
|
||||
// Single click handler for both item and radio button
|
||||
val onClickAction = {
|
||||
@ -90,6 +92,11 @@ class ShippingAdapter(
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatCurrency(amount: Double): String {
|
||||
val formatter = NumberFormat.getCurrencyInstance(Locale("in", "ID"))
|
||||
return formatter.format(amount).replace(",00", "")
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return courierCostsList.sumOf { it.services.size }
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
package com.alya.ecommerce_serang.ui.order.detail
|
||||
|
||||
import android.Manifest
|
||||
import android.R
|
||||
import android.app.Activity
|
||||
import android.app.AlertDialog
|
||||
import android.app.DatePickerDialog
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
@ -12,6 +12,7 @@ import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.webkit.MimeTypeMap
|
||||
import android.widget.AdapterView
|
||||
import android.widget.ArrayAdapter
|
||||
@ -25,6 +26,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.dto.AddEvidenceMultipartRequest
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||
import com.alya.ecommerce_serang.data.repository.OrderRepository
|
||||
@ -37,6 +39,7 @@ import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import java.io.File
|
||||
import java.text.NumberFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
@ -59,11 +62,9 @@ class AddEvidencePaymentActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private val paymentMethods = arrayOf(
|
||||
"Pilih metode pembayaran",
|
||||
"Transfer Bank",
|
||||
"E-Wallet",
|
||||
"Virtual Account",
|
||||
"Cash on Delivery"
|
||||
"QRIS",
|
||||
)
|
||||
|
||||
// private val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
|
||||
@ -128,11 +129,8 @@ class AddEvidencePaymentActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun setupUI() {
|
||||
// Set product details\
|
||||
|
||||
// Setup payment methods spinner
|
||||
val adapter = ArrayAdapter(this, R.layout.simple_spinner_item, paymentMethods)
|
||||
adapter.setDropDownViewResource(R.layout.simple_spinner_dropdown_item)
|
||||
val paymentMethods = listOf("Transfer Bank", "COD", "QRIS")
|
||||
val adapter = SpinnerCardAdapter(this, paymentMethods)
|
||||
binding.spinnerPaymentMethod.adapter = adapter
|
||||
}
|
||||
|
||||
@ -219,15 +217,23 @@ class AddEvidencePaymentActivity : AppCompatActivity() {
|
||||
private fun showImagePickerOptions() {
|
||||
val options = arrayOf(
|
||||
"Pilih dari Galeri",
|
||||
"Batal"
|
||||
"Kembali"
|
||||
)
|
||||
|
||||
androidx.appcompat.app.AlertDialog.Builder(this)
|
||||
.setTitle("Pilih Bukti Pembayaran")
|
||||
.setItems(options) { dialog, which ->
|
||||
val adapter = object : ArrayAdapter<String>(this, R.layout.item_dialog_add_evidence, R.id.tvOption, options) {
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
val view = super.getView(position, convertView, parent)
|
||||
val divider = view.findViewById<View>(R.id.divider)
|
||||
divider.visibility = if (position == count - 1) View.GONE else View.VISIBLE
|
||||
return view
|
||||
}
|
||||
}
|
||||
|
||||
AlertDialog.Builder(this)
|
||||
.setAdapter(adapter) { dialog, which ->
|
||||
when (which) {
|
||||
0 -> openGallery() // Gallery
|
||||
1 -> dialog.dismiss() // Cancel
|
||||
0 -> openGallery()
|
||||
1 -> dialog.dismiss()
|
||||
}
|
||||
}
|
||||
.show()
|
||||
@ -440,6 +446,8 @@ class AddEvidencePaymentActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
companion object {
|
||||
private const val PERMISSION_REQUEST_CODE = 100
|
||||
private const val TAG = "AddEvidenceActivity"
|
||||
|
@ -17,6 +17,7 @@ import com.alya.ecommerce_serang.data.repository.OrderRepository
|
||||
import com.alya.ecommerce_serang.databinding.ActivityPaymentBinding
|
||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||
import com.alya.ecommerce_serang.utils.SessionManager
|
||||
import java.text.NumberFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
@ -100,6 +101,7 @@ class PaymentActivity : AppCompatActivity() {
|
||||
// Log.d(TAG, "Button clicked - showing toast")
|
||||
// Toast.makeText(this@PaymentActivity, "Button works! OrderID: $orderId", Toast.LENGTH_LONG).show()
|
||||
// }
|
||||
|
||||
binding.btnUploadPaymentProof.apply {
|
||||
isEnabled = true
|
||||
isClickable = true
|
||||
@ -134,7 +136,7 @@ class PaymentActivity : AppCompatActivity() {
|
||||
Log.d(TAG, "Order details received: $order")
|
||||
|
||||
// Set total amount
|
||||
binding.tvTotalAmount.text = order.totalAmount ?: "Rp0"
|
||||
binding.tvTotalAmount.text = formatCurrency(order.totalAmount?.toDouble() ?: 0.00)
|
||||
Log.d(TAG, "Total Amount: ${order.totalAmount}")
|
||||
|
||||
|
||||
@ -202,6 +204,11 @@ class PaymentActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatCurrency(amount: Double): String {
|
||||
val formatter = NumberFormat.getCurrencyInstance(Locale("in", "ID"))
|
||||
return formatter.format(amount).replace(",00", "")
|
||||
}
|
||||
|
||||
private fun showInstructions(type: String) {
|
||||
// Implementasi tampilkan instruksi
|
||||
val instructions = when (type) {
|
||||
|
@ -0,0 +1,33 @@
|
||||
package com.alya.ecommerce_serang.ui.order.detail
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.TextView
|
||||
import com.alya.ecommerce_serang.R
|
||||
|
||||
class SpinnerCardAdapter(
|
||||
context: Context,
|
||||
private val items: List<String>
|
||||
) : ArrayAdapter<String>(context, 0, items) {
|
||||
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
// View shown when Spinner is collapsed
|
||||
return createCardView(position, convertView, parent)
|
||||
}
|
||||
|
||||
override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
// View shown for dropdown items
|
||||
return createCardView(position, convertView, parent)
|
||||
}
|
||||
|
||||
private fun createCardView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val view = convertView ?: inflater.inflate(R.layout.item_dialog_spinner_card, parent, false)
|
||||
val textView = view.findViewById<TextView>(R.id.tvOption)
|
||||
textView.text = items[position]
|
||||
return view
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
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.order.OrderListItemsItem
|
||||
import com.bumptech.glide.Glide
|
||||
@ -39,11 +40,18 @@ class DetailOrderItemsAdapter : RecyclerView.Adapter<DetailOrderItemsAdapter.Det
|
||||
private val tvProductName: TextView = itemView.findViewById(R.id.tvProductName)
|
||||
private val tvQuantity: TextView = itemView.findViewById(R.id.tvQuantity)
|
||||
private val tvPrice: TextView = itemView.findViewById(R.id.tvPrice)
|
||||
private val tvStoreName: TextView = itemView.findViewById(R.id.tvStoreName)
|
||||
|
||||
fun bind(item: OrderListItemsItem) {
|
||||
|
||||
val fullImageUrl = if (item.productImage.startsWith("/")) {
|
||||
BASE_URL + item.productImage.removePrefix("/") // Append base URL if the path starts with "/"
|
||||
} else {
|
||||
item.productImage // Use as is if it's already a full URL
|
||||
}
|
||||
// Load product image
|
||||
Glide.with(itemView.context)
|
||||
.load(item.productImage)
|
||||
.load(fullImageUrl)
|
||||
.placeholder(R.drawable.placeholder_image)
|
||||
.error(R.drawable.placeholder_image)
|
||||
.into(ivProduct)
|
||||
@ -52,7 +60,8 @@ class DetailOrderItemsAdapter : RecyclerView.Adapter<DetailOrderItemsAdapter.Det
|
||||
|
||||
tvProductName.text = item.productName
|
||||
tvQuantity.text = "${item.quantity} buah"
|
||||
tvPrice.text = "Rp${newPrice}"
|
||||
tvPrice.text = newPrice
|
||||
tvStoreName.text = item.storeName
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,7 @@ import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import com.google.gson.Gson
|
||||
import java.io.File
|
||||
import java.text.NumberFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
@ -172,7 +173,6 @@ class DetailOrderStatusActivity : AppCompatActivity() {
|
||||
try {
|
||||
// Set order date and payment deadline
|
||||
binding.tvOrderDate.text = formatDate(orders.createdAt)
|
||||
binding.tvPaymentDeadline.text = formatDatePay(orders.updatedAt)
|
||||
|
||||
Log.d(TAG, "populateOrderDetails: Order created at ${orders.createdAt}, formatted as ${binding.tvOrderDate.text}")
|
||||
|
||||
@ -197,10 +197,10 @@ class DetailOrderStatusActivity : AppCompatActivity() {
|
||||
Log.d(TAG, "populateOrderDetails: Payment method=${orders.payInfoName ?: "Tidak tersedia"}")
|
||||
|
||||
// Set subtotal, shipping cost, and total
|
||||
val subtotal = orders.totalAmount?.toIntOrNull()?.minus(orders.shipmentPrice.toIntOrNull() ?: 0) ?: 0
|
||||
binding.tvSubtotal.text = "Rp$subtotal"
|
||||
binding.tvShippingCost.text = "Rp${orders.shipmentPrice}"
|
||||
binding.tvTotal.text = "Rp${orders.totalAmount}"
|
||||
val subtotal = orders.totalAmount?.minus(orders.shipmentPrice.toIntOrNull() ?: 0) ?: 0
|
||||
binding.tvSubtotal.text = formatCurrency(subtotal.toDouble())
|
||||
binding.tvShippingCost.text = formatCurrency(orders.shipmentPrice.toDouble())
|
||||
binding.tvTotal.text = formatCurrency(orders.totalAmount?.toDouble() ?: 0.00)
|
||||
|
||||
Log.d(TAG, "populateOrderDetails: Subtotal=$subtotal, Shipping=${orders.shipmentPrice}, Total=${orders.totalAmount}")
|
||||
|
||||
@ -255,6 +255,8 @@ class DetailOrderStatusActivity : AppCompatActivity() {
|
||||
binding.tvStatusHeader.text = "Belum Dibayar"
|
||||
binding.tvStatusNote.visibility = View.VISIBLE
|
||||
binding.tvStatusNote.text = "Pesanan ini harus dibayar sebelum ${formatDatePay(orders.updatedAt)}"
|
||||
binding.tvPaymentDeadlineLabel.text = "Batas Bayar:"
|
||||
binding.tvPaymentDeadline.text = formatDatePay(orders.updatedAt)
|
||||
|
||||
// Set buttons
|
||||
binding.btnSecondary.apply {
|
||||
@ -286,6 +288,8 @@ class DetailOrderStatusActivity : AppCompatActivity() {
|
||||
binding.tvStatusHeader.text = "Sudah Dibayar"
|
||||
binding.tvStatusNote.visibility = View.VISIBLE
|
||||
binding.tvStatusNote.text = "Menunggu pesanan dikonfirmasi penjual ${formatDatePay(orders.updatedAt)}"
|
||||
binding.tvPaymentDeadlineLabel.text = "Batas konfirmasi penjual:"
|
||||
binding.tvPaymentDeadline.text = formatDatePay(orders.updatedAt)
|
||||
|
||||
// Set buttons
|
||||
binding.btnSecondary.apply {
|
||||
@ -304,6 +308,8 @@ class DetailOrderStatusActivity : AppCompatActivity() {
|
||||
binding.tvStatusHeader.text = "Sedang Diproses"
|
||||
binding.tvStatusNote.visibility = View.VISIBLE
|
||||
binding.tvStatusNote.text = "Penjual sedang memproses pesanan Anda"
|
||||
binding.tvPaymentDeadlineLabel.text = "Batas diproses penjual:"
|
||||
binding.tvPaymentDeadline.text = formatDatePay(orders.updatedAt)
|
||||
|
||||
binding.btnSecondary.apply {
|
||||
visibility = View.VISIBLE
|
||||
@ -326,6 +332,8 @@ class DetailOrderStatusActivity : AppCompatActivity() {
|
||||
binding.tvStatusHeader.text = "Sudah Dikirim"
|
||||
binding.tvStatusNote.visibility = View.VISIBLE
|
||||
binding.tvStatusNote.text = "Pesanan Anda sedang dalam perjalanan. Akan sampai sekitar ${formatShipmentDate(orders.updatedAt, orders.etd ?: "0")}"
|
||||
binding.tvPaymentDeadlineLabel.text = "Estimasi pesanan sampai:"
|
||||
binding.tvPaymentDeadline.text = formatShipmentDate(orders.updatedAt, orders.etd ?: "0")
|
||||
|
||||
binding.btnSecondary.apply {
|
||||
visibility = View.VISIBLE
|
||||
@ -358,6 +366,8 @@ class DetailOrderStatusActivity : AppCompatActivity() {
|
||||
|
||||
binding.tvStatusHeader.text = "Pesanan Selesai"
|
||||
binding.tvStatusNote.visibility = View.GONE
|
||||
binding.tvPaymentDeadlineLabel.text = "Pesanan selesai:"
|
||||
binding.tvPaymentDeadline.text = formatDate(orders.autoCompletedAt.toString())
|
||||
|
||||
binding.btnPrimary.apply {
|
||||
visibility = View.VISIBLE
|
||||
@ -379,6 +389,8 @@ class DetailOrderStatusActivity : AppCompatActivity() {
|
||||
binding.tvStatusHeader.text = "Pesanan Selesai"
|
||||
binding.tvStatusNote.visibility = View.VISIBLE
|
||||
binding.tvStatusNote.text = "Pesanan dibatalkan: ${orders.cancelReason ?: "Alasan tidak diberikan"}"
|
||||
binding.tvPaymentDeadlineLabel.text = "Tanggal dibatalkan: "
|
||||
binding.tvPaymentDeadline.text = formatDate(orders.cancelDate.toString())
|
||||
|
||||
binding.btnSecondary.apply {
|
||||
visibility = View.GONE
|
||||
@ -604,7 +616,8 @@ class DetailOrderStatusActivity : AppCompatActivity() {
|
||||
val inputFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault())
|
||||
inputFormat.timeZone = TimeZone.getTimeZone("UTC")
|
||||
|
||||
val outputFormat = SimpleDateFormat("HH:mm dd MMMM yyyy", Locale("id", "ID"))
|
||||
val timeFormat = SimpleDateFormat("HH:mm", Locale("id", "ID"))
|
||||
val dateFormat = SimpleDateFormat("dd MMMM yyyy", Locale("id", "ID"))
|
||||
|
||||
val date = inputFormat.parse(dateString)
|
||||
|
||||
@ -614,7 +627,10 @@ class DetailOrderStatusActivity : AppCompatActivity() {
|
||||
calendar.set(Calendar.HOUR_OF_DAY, 23)
|
||||
calendar.set(Calendar.MINUTE, 59)
|
||||
|
||||
val formatted = outputFormat.format(calendar.time)
|
||||
val timePart = timeFormat.format(calendar.time)
|
||||
val datePart = dateFormat.format(calendar.time)
|
||||
|
||||
val formatted = "$timePart\n$datePart"
|
||||
Log.d(TAG, "formatDate: Formatted date: $formatted")
|
||||
formatted
|
||||
} ?: dateString
|
||||
@ -640,9 +656,13 @@ class DetailOrderStatusActivity : AppCompatActivity() {
|
||||
calendar.add(Calendar.HOUR, 24)
|
||||
val dueDate = calendar.time
|
||||
|
||||
// Format due date for display
|
||||
val dueDateFormat = SimpleDateFormat("dd MMM yyyy", Locale.getDefault())
|
||||
val formatted = dueDateFormat.format(calendar.time)
|
||||
val timeFormat = SimpleDateFormat("HH:mm", Locale("id", "ID"))
|
||||
val dateFormat = SimpleDateFormat("dd MMM yyyy", Locale("id", "ID"))
|
||||
|
||||
val timePart = timeFormat.format(dueDate)
|
||||
val datePart = dateFormat.format(dueDate)
|
||||
|
||||
val formatted = "$timePart\n$datePart"
|
||||
|
||||
Log.d(TAG, "formatDatePay: Formatted payment date: $formatted")
|
||||
formatted
|
||||
@ -729,6 +749,11 @@ class DetailOrderStatusActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatCurrency(amount: Double): String {
|
||||
val formatter = NumberFormat.getCurrencyInstance(Locale("in", "ID"))
|
||||
return formatter.format(amount).replace(",00", "")
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
Log.d(TAG, "onDestroy: Cleaning up references")
|
||||
super.onDestroy()
|
||||
|
@ -11,6 +11,8 @@ import com.alya.ecommerce_serang.data.api.dto.ProductsItem
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.product.StoreItem
|
||||
import com.alya.ecommerce_serang.databinding.ItemProductHorizontalBinding
|
||||
import com.bumptech.glide.Glide
|
||||
import java.text.NumberFormat
|
||||
import java.util.Locale
|
||||
|
||||
class OtherProductAdapter (
|
||||
private var products: List<ProductsItem>,
|
||||
@ -32,7 +34,7 @@ class OtherProductAdapter (
|
||||
Log.d("ProductAdapter", "Loading image: $fullImageUrl")
|
||||
|
||||
tvProductName.text = product.name
|
||||
tvProductPrice.text = product.price
|
||||
tvProductPrice.text = formatCurrency(product.price.toDouble())
|
||||
rating.text = product.rating
|
||||
|
||||
// Load image using Glide
|
||||
@ -77,6 +79,11 @@ class OtherProductAdapter (
|
||||
diffResult.dispatchUpdatesTo(this)
|
||||
}
|
||||
|
||||
private fun formatCurrency(amount: Double): String {
|
||||
val formatter = NumberFormat.getCurrencyInstance(Locale("in", "ID"))
|
||||
return formatter.format(amount).replace(",00", "")
|
||||
}
|
||||
|
||||
class ProductDiffCallback(
|
||||
private val oldList: List<ProductsItem>,
|
||||
private val newList: List<ProductsItem>
|
||||
|
@ -18,6 +18,7 @@ import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||
import com.alya.ecommerce_serang.data.repository.ProductRepository
|
||||
import com.alya.ecommerce_serang.data.repository.Result
|
||||
import com.alya.ecommerce_serang.databinding.ActivityListProductBinding
|
||||
import com.alya.ecommerce_serang.ui.cart.CartActivity
|
||||
import com.alya.ecommerce_serang.ui.product.DetailProductActivity
|
||||
import com.alya.ecommerce_serang.ui.product.ProductUserViewModel
|
||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||
@ -60,13 +61,23 @@ class ListProductActivity : AppCompatActivity() {
|
||||
windowInsets
|
||||
}
|
||||
|
||||
|
||||
setupToolbar()
|
||||
setupObserver()
|
||||
setupRecyclerView()
|
||||
viewModel.loadProductsList()
|
||||
|
||||
}
|
||||
|
||||
private fun setupToolbar(){
|
||||
binding.searchContainerList.btnBack.setOnClickListener{
|
||||
finish()
|
||||
}
|
||||
binding.searchContainerList.btnCart.setOnClickListener{
|
||||
val intent = Intent(this, CartActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
|
||||
binding.rvProductsList.apply {
|
||||
|
@ -11,6 +11,8 @@ import com.alya.ecommerce_serang.data.api.dto.ProductsItem
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.product.StoreItem
|
||||
import com.alya.ecommerce_serang.databinding.ItemProductGridBinding
|
||||
import com.bumptech.glide.Glide
|
||||
import java.text.NumberFormat
|
||||
import java.util.Locale
|
||||
|
||||
class ListProductAdapter(
|
||||
private var products: List<ProductsItem>,
|
||||
@ -24,7 +26,7 @@ class ListProductAdapter(
|
||||
fun bind(product: ProductsItem) = with(binding) {
|
||||
|
||||
tvProductName.text = product.name
|
||||
tvProductPrice.text = product.price
|
||||
tvProductPrice.text = formatCurrency(product.price.toDouble())
|
||||
rating.text = product.rating
|
||||
|
||||
val fullImageUrl = if (product.image.startsWith("/")) {
|
||||
@ -68,6 +70,11 @@ class ListProductAdapter(
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
private fun formatCurrency(amount: Double): String {
|
||||
val formatter = NumberFormat.getCurrencyInstance(Locale("in", "ID"))
|
||||
return formatter.format(amount).replace(",00", "")
|
||||
}
|
||||
|
||||
class ProductDiffCallback(
|
||||
private val oldList: List<ProductsItem>,
|
||||
private val newList: List<ProductsItem>
|
||||
|
@ -378,7 +378,9 @@ class ChatStoreActivity : AppCompatActivity() {
|
||||
|
||||
// Update messages
|
||||
val previousCount = chatAdapter.itemCount
|
||||
chatAdapter.submitList(state.messages) {
|
||||
val displayItems = viewModel.getDisplayItems()
|
||||
|
||||
chatAdapter.submitList(displayItems) {
|
||||
Log.d(TAG, "Messages submitted to adapter")
|
||||
// Only auto-scroll for new messages or initial load
|
||||
if (previousCount == 0 || state.messages.size > previousCount) {
|
||||
|
6
app/src/main/res/drawable/bg_date_header.xml
Normal file
6
app/src/main/res/drawable/bg_date_header.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/black_200" />
|
||||
<corners android:radius="12dp" />
|
||||
</shape>
|
@ -4,7 +4,6 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Theme.Ecommerce_serang"
|
||||
tools:context=".ui.product.listproduct.ListProductActivity">
|
||||
|
||||
<include
|
||||
|
@ -30,7 +30,9 @@
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/search_background"
|
||||
android:iconifiedByDefault="false"
|
||||
android:queryHint="Search products..." />
|
||||
android:queryHint="Search products..."
|
||||
app:iconifiedByDefault="false"
|
||||
app:queryHint="Search products..." />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
21
app/src/main/res/layout/item_date_header.xml
Normal file
21
app/src/main/res/layout/item_date_header.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center"
|
||||
android:paddingVertical="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvDate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_date_header"
|
||||
android:paddingHorizontal="12dp"
|
||||
android:paddingVertical="4dp"
|
||||
android:text="Today"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</LinearLayout>
|
34
app/src/main/res/layout/item_dialog_add_evidence.xml
Normal file
34
app/src/main/res/layout/item_dialog_add_evidence.xml
Normal file
@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
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">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvOption"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/dmsans_semibold"
|
||||
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>
|
21
app/src/main/res/layout/item_dialog_spinner_card.xml
Normal file
21
app/src/main/res/layout/item_dialog_spinner_card.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
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:cardElevation="4dp"
|
||||
card_view:cardCornerRadius="8dp"
|
||||
android:foreground="?android:attr/selectableItemBackground">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvOption"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp"
|
||||
android:textColor="@color/black_500"
|
||||
android:text="Item 1"
|
||||
android:fontFamily="@font/dmsans_semibold"
|
||||
android:textSize="16sp"
|
||||
android:gravity="center_vertical" />
|
||||
</androidx.cardview.widget.CardView>
|
@ -4,7 +4,7 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp">
|
||||
android:paddingHorizontal="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvStoreName"
|
||||
@ -68,6 +68,7 @@
|
||||
android:textColor="@color/blue_500"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:paddingBottom="4dp"
|
||||
app:layout_constraintStart_toEndOf="@+id/ivProduct"
|
||||
app:layout_constraintTop_toBottomOf="@+id/tvQuantity" />
|
||||
|
||||
|
Reference in New Issue
Block a user