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 7dc5bf5..181f0e5 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 @@ -5,14 +5,17 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Button +import android.widget.ImageView import android.widget.TextView -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.response.store.orders.OrdersItem import com.alya.ecommerce_serang.ui.profile.mystore.sells.payment.DetailPaymentActivity import com.alya.ecommerce_serang.ui.profile.mystore.sells.shipment.DetailShipmentActivity import com.alya.ecommerce_serang.utils.viewmodel.SellsViewModel +import com.bumptech.glide.Glide import com.google.android.material.button.MaterialButton import com.google.gson.Gson import java.text.SimpleDateFormat @@ -60,59 +63,72 @@ class SellsAdapter( override fun getItemCount(): Int = sells.size inner class SellsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - private val tvStoreName: TextView = itemView.findViewById(R.id.tvUserName) - private val rvOrderItems: RecyclerView = itemView.findViewById(R.id.rvSellsItems) - private val tvShowMore: TextView = itemView.findViewById(R.id.tvShowMores) - private val tvTotalAmount: TextView = itemView.findViewById(R.id.tvTotalAmounts) - private val tvItemCountLabel: TextView = itemView.findViewById(R.id.tv_count_total_items) + private val layoutOrders: View = itemView.findViewById(R.id.layout_orders) + private val layoutPayments: View = itemView.findViewById(R.id.layout_payments) + private val layoutShipments: View = itemView.findViewById(R.id.layout_shipments) + + private var tvSellsTitle: TextView = itemView.findViewById(R.id.tv_payment_title) + private var tvSellsNumber: TextView = itemView.findViewById(R.id.tv_payment_number) + private var tvSellsDueDesc: TextView = itemView.findViewById(R.id.tv_payment_due_desc) + private var tvSellsDue: TextView = itemView.findViewById(R.id.tv_payment_due) + private var tvSellsLocation: TextView = itemView.findViewById(R.id.tv_payment_location) + private var tvSellsCustomer: TextView = itemView.findViewById(R.id.tv_payment_customer) + private var ivSellsProduct: ImageView = itemView.findViewById(R.id.iv_payment_product) + private var tvSellsProductName: TextView = itemView.findViewById(R.id.tv_payment_product_name) + private var tvSellsProductQty: TextView = itemView.findViewById(R.id.tv_payment_product_qty) + private var tvSellsProductPrice: TextView = itemView.findViewById(R.id.tv_payment_product_price) + private var tvSeeMore: TextView = itemView.findViewById(R.id.tv_see_more_payment) + private var tvSellsQty: TextView = itemView.findViewById(R.id.tv_payment_qty) + private var tvSellsPrice: TextView = itemView.findViewById(R.id.tv_payment_price) + private val btnConfirmPayment: Button = itemView.findViewById(R.id.btn_confirm_payment) + private val btnConfirmShipment: Button = itemView.findViewById(R.id.btn_confirm_shipment) fun bind(order: OrdersItem) { Log.d("SellsAdapter", "=== ViewHolder.bind() called ===") Log.d("SellsAdapter", "Binding order: ${order.orderId} with status: ${order.status}") - // Show customer/buyer name (seller's perspective) - tvStoreName.text = order.username ?: "Unknown Customer" - Log.d("SellsAdapter", "Customer name set: ${order.username}") + val actualStatus = if (fragmentStatus == "all") order.status ?: "" else fragmentStatus + adjustDisplay(actualStatus, order) - // Set total amount - tvTotalAmount.text = "Rp${order.totalAmount}" - Log.d("SellsAdapter", "Total amount set: ${order.totalAmount}") + tvSellsNumber.text = "No. Pesanan: ${order.orderId}" + tvSellsLocation.text = order.subdistrict + tvSellsCustomer.text = order.username - // Set item count - val itemCount = order.orderItems?.size ?: 0 - tvItemCountLabel.text = itemView.context.getString(R.string.item_count_prod, itemCount) - Log.d("SellsAdapter", "Item count set: $itemCount") + val product = order.orderItems?.firstOrNull() + tvSellsProductName.text = product?.productName + tvSellsProductQty.text = "x${product?.quantity}" + tvSellsProductPrice.text = formatPrice(product?.price.toString()) - // Set up the order items RecyclerView - val productAdapter = SellsProductAdapter() - rvOrderItems.apply { - layoutManager = LinearLayoutManager(itemView.context) - adapter = productAdapter + val fullImageUrl = when (val img = product?.productImage) { + is String -> { + if (img.startsWith("/")) BASE_URL + img.substring(1) else img + } + else -> R.drawable.placeholder_image } - Log.d("SellsAdapter", "Product RecyclerView configured") + // Load product image using Glide + Glide.with(itemView.context) + .load(fullImageUrl) + .placeholder(R.drawable.placeholder_image) + .error(R.drawable.placeholder_image) + .into(ivSellsProduct) // Display only the first product and show "View more" for the rest order.orderItems?.let { items -> if (items.isNotEmpty()) { - productAdapter.submitList(items.take(1)) - Log.d("SellsAdapter", "Product list submitted: ${items.size} items") - // Show or hide the "View more" text based on number of items if (items.size > 1) { val itemString = items.size - 1 - tvShowMore.visibility = View.VISIBLE - tvShowMore.text = itemView.context.getString(R.string.show_more_product, itemString) - Log.d("SellsAdapter", "Show more visible: $itemString more items") + tvSeeMore.visibility = View.VISIBLE + tvSeeMore.text = itemView.context.getString(R.string.show_more_product, itemString) } else { - tvShowMore.visibility = View.GONE - Log.d("SellsAdapter", "Show more hidden: only 1 item") + tvSeeMore.visibility = View.GONE } } else { - tvShowMore.visibility = View.GONE + tvSeeMore.visibility = View.GONE Log.w("SellsAdapter", "Order has no items!") } } ?: run { - tvShowMore.visibility = View.GONE + tvSeeMore.visibility = View.GONE Log.w("SellsAdapter", "Order items is null!") } @@ -121,192 +137,169 @@ class SellsAdapter( onOrderClickListener(order) } - val actualStatus = if (fragmentStatus == "all") order.status ?: "" else fragmentStatus - Log.d("SellsAdapter", "Adjusting UI for status: '$actualStatus' (fragmentStatus: '$fragmentStatus')") - adjustButtonsAndText(actualStatus, order) - Log.d("SellsAdapter", "=== ViewHolder.bind() completed ===") } - private fun adjustButtonsAndText(status: String, order: OrdersItem) { - Log.d("SellsAdapter", "Adjusting buttons for status: $status") - - // Get references to buttons and status views - val btnLeft = itemView.findViewById(R.id.btn_left) - val btnRight = itemView.findViewById(R.id.btn_right) - val statusOrder = itemView.findViewById(R.id.tvOrderStatus) - val deadlineLabel = itemView.findViewById(R.id.tvDeadlineLabel) - val deadlineDate = itemView.findViewById(R.id.tvDeadlineDate) + private fun adjustDisplay(status: String, order: OrdersItem) { + Log.d("SellsAdapter", "Adjusting display for status: $status") // Reset visibility - btnLeft.visibility = View.GONE - btnRight.visibility = View.GONE - statusOrder.visibility = View.GONE - deadlineLabel.visibility = View.GONE - deadlineDate.visibility = View.GONE + layoutOrders.visibility = View.GONE + layoutPayments.visibility = View.VISIBLE + layoutShipments.visibility = View.GONE when (status) { - "pending" -> { - statusOrder.apply { - visibility = View.VISIBLE - text = "Menunggu Tagihan" - } - deadlineLabel.apply { - visibility = View.VISIBLE - text = "Batas waktu konfirmasi:" - } - deadlineDate.apply { - visibility = View.VISIBLE - text = formatDate(order.createdAt ?: "") - } - btnLeft.apply { - visibility = View.VISIBLE - text = "Tolak Pesanan" - setOnClickListener { - // Handle reject order - viewModel.updateOrderStatus(order.orderId, "canceled") - viewModel.refreshOrders() - } - } - btnRight.apply { - visibility = View.VISIBLE - text = "Terima Pesanan" - setOnClickListener { - // Handle accept order - viewModel.updateOrderStatus(order.orderId, "unpaid") - viewModel.refreshOrders() - } - } - } - "unpaid" -> { - statusOrder.apply { - visibility = View.VISIBLE - text = "Konfirmasi Bayar" - } - deadlineLabel.apply { - visibility = View.VISIBLE - text = "Batas pembayaran:" - } - deadlineDate.apply { - visibility = View.VISIBLE - text = formatDatePay(order.updatedAt ?: "") - } - btnLeft.apply { - visibility = View.VISIBLE - text = "Batalkan" - setOnClickListener { - viewModel.updateOrderStatus(order.orderId, "canceled") - viewModel.refreshOrders() + "pending", "unpaid" -> { + layoutOrders.visibility = View.VISIBLE + layoutPayments.visibility = View.GONE + layoutShipments.visibility = View.GONE + + tvSellsTitle = itemView.findViewById(R.id.tv_order_title) + tvSellsNumber = itemView.findViewById(R.id.tv_order_number) + tvSellsDue = itemView.findViewById(R.id.tv_order_due) + tvSellsCustomer = itemView.findViewById(R.id.tv_order_customer) + tvSellsProductName = itemView.findViewById(R.id.tv_order_product_name) + tvSellsProductQty = itemView.findViewById(R.id.tv_order_product_qty) + tvSellsProductPrice = itemView.findViewById(R.id.tv_order_product_price) + tvSeeMore = itemView.findViewById(R.id.tv_see_more_order) + tvSellsQty = itemView.findViewById(R.id.tv_order_qty) + tvSellsPrice = itemView.findViewById(R.id.tv_order_price) + + tvSellsDue.text = formatDueDate(order.updatedAt.toString(), 1) + + val product = order.orderItems?.firstOrNull() + tvSellsProductName.text = product?.productName + tvSellsProductQty.text = "x${product?.quantity}" + tvSellsProductPrice.text = formatPrice(product?.price.toString()) + + val fullImageUrl = when (val img = product?.productImage) { + is String -> { + if (img.startsWith("/")) BASE_URL + img.substring(1) else img } + else -> R.drawable.placeholder_image } + // Load product image using Glide + Glide.with(itemView.context) + .load(fullImageUrl) + .placeholder(R.drawable.placeholder_image) + .error(R.drawable.placeholder_image) + .into(ivSellsProduct) + + tvSellsQty.text = "${order.orderItems?.size} produk" + tvSellsPrice.text = formatPrice(order.totalAmount.toString()) } "paid" -> { - statusOrder.apply { - visibility = View.VISIBLE - text = "Sudah Dibayar" - } - deadlineLabel.apply { - visibility = View.VISIBLE - text = "Konfirmasi pembayaran sebelum:" - } - deadlineDate.apply { - visibility = View.VISIBLE - text = formatDatePay(order.updatedAt ?: "") - } - btnRight.apply { - visibility = View.VISIBLE - text = "Konfirmasi Pembayaran" - setOnClickListener { - val context = itemView.context - val intent = Intent(context, DetailPaymentActivity::class.java) - intent.putExtra("sells_data", Gson().toJson(order)) - context.startActivity(intent) - } + layoutOrders.visibility = View.GONE + layoutPayments.visibility = View.VISIBLE + layoutShipments.visibility = View.GONE + + tvSellsDue.text = formatDueDate(order.updatedAt.toString(), 2) + btnConfirmPayment.setOnClickListener { + + val context = itemView.context + val intent = Intent(context, DetailPaymentActivity::class.java) + intent.putExtra("sells_data", Gson().toJson(order)) + context.startActivity(intent) + viewModel.refreshOrders() } + + tvSellsTitle.text = "Pesanan Telah Dibayar" + tvSellsDueDesc.text = "Konfirmasi pembayaran sebelum:" + tvSellsDue.text = formatDueDate(order.updatedAt.toString(), 2) + } "processed" -> { - statusOrder.apply { - visibility = View.VISIBLE - text = "Diproses" - } - deadlineLabel.apply { - visibility = View.VISIBLE - text = "Kirim sebelum:" - } - deadlineDate.apply { - visibility = View.VISIBLE - text = formatDatePay(order.updatedAt ?: "") - } - btnRight.apply { - visibility = View.VISIBLE - text = "Kirim Pesanan" - setOnClickListener { - val context = itemView.context - val intent = Intent(context, DetailShipmentActivity::class.java) - intent.putExtra("sells_data", Gson().toJson(order)) - context.startActivity(intent) - } + layoutOrders.visibility = View.GONE + layoutPayments.visibility = View.GONE + layoutShipments.visibility = View.VISIBLE + + tvSellsTitle = itemView.findViewById(R.id.tv_shipment_title) + tvSellsNumber = itemView.findViewById(R.id.tv_shipment_number) + tvSellsDue = itemView.findViewById(R.id.tv_shipment_due) + tvSellsLocation = itemView.findViewById(R.id.tv_shipment_location) + tvSellsCustomer = itemView.findViewById(R.id.tv_shipment_customer) + tvSellsProductName = itemView.findViewById(R.id.tv_shipment_product_name) + tvSellsProductQty = itemView.findViewById(R.id.tv_shipment_product_qty) + tvSeeMore = itemView.findViewById(R.id.tv_see_more_shipment) + + tvSellsDue.text = formatDueDate(order.updatedAt.toString(), 2) + btnConfirmShipment.setOnClickListener { + val context = itemView.context + val intent = Intent(context, DetailShipmentActivity::class.java) + intent.putExtra("sells_data", Gson().toJson(order)) + context.startActivity(intent) } + + tvSellsTitle.text = "Pesanan Perlu Dikirim" + tvSellsNumber.text = "No. Pesanan: ${order.orderId}" + tvSellsLocation.text = order.subdistrict + tvSellsCustomer.text = order.username + tvSellsDue.text = formatDueDate(order.updatedAt.toString(), 2) } "shipped" -> { - statusOrder.apply { - visibility = View.VISIBLE - text = "Dikirim" - } - deadlineLabel.apply { - visibility = View.VISIBLE - text = "Dikirimkan pada:" - } - deadlineDate.apply { - visibility = View.VISIBLE - text = formatDate(order.updatedAt ?: "") - } + layoutOrders.visibility = View.GONE + layoutPayments.visibility = View.VISIBLE + layoutShipments.visibility = View.GONE + + tvSellsTitle.text = "Pesanan Telah Dikirim" + tvSellsDueDesc.text = "Dikirimkan pada" + + tvSellsDue.text = formatDueDate(order.updatedAt.toString(), 0) + tvSellsDue.background = itemView.context.getDrawable(R.drawable.bg_product_inactive) + btnConfirmPayment.visibility = View.GONE + } + "delivered" -> { + layoutOrders.visibility = View.GONE + layoutPayments.visibility = View.VISIBLE + layoutShipments.visibility = View.GONE + + tvSellsTitle.text = "Pesanan Telah Dikirim" + tvSellsDueDesc.text = "Dikirimkan pada" + + tvSellsDue.text = formatDueDate(order.updatedAt.toString(), 0) + tvSellsDue.background = itemView.context.getDrawable(R.drawable.bg_product_inactive) + btnConfirmPayment.visibility = View.GONE } "completed" -> { - statusOrder.apply { - visibility = View.VISIBLE - text = "Selesai" - } - deadlineLabel.apply { - visibility = View.VISIBLE - text = "Selesai pada:" - } - deadlineDate.apply { - visibility = View.VISIBLE - text = formatDate(order.updatedAt ?: "") - } + layoutOrders.visibility = View.GONE + layoutPayments.visibility = View.VISIBLE + layoutShipments.visibility = View.GONE + + tvSellsTitle.text = "Pesanan Selesai" + tvSellsDueDesc.text = "Selesai pada" + + tvSellsDue.text = formatDueDate(order.updatedAt.toString(), 0) + tvSellsDue.background = itemView.context.getDrawable(R.drawable.bg_product_inactive) + btnConfirmPayment.visibility = View.GONE } "canceled" -> { - statusOrder.apply { - visibility = View.VISIBLE - text = "Dibatalkan" - } - deadlineLabel.apply { - visibility = View.VISIBLE - text = "Dibatalkan pada:" - } - deadlineDate.apply { - visibility = View.VISIBLE - text = formatDate(order.cancelDate ?: "") - } + layoutOrders.visibility = View.GONE + layoutPayments.visibility = View.VISIBLE + layoutShipments.visibility = View.GONE + + tvSellsTitle.text = "Pesanan Dibatalkan" + tvSellsDueDesc.text = "Dibatalkan pada" + + tvSellsDue.text = formatDueDate(order.updatedAt.toString(), 0) + tvSellsDue.background = itemView.context.getDrawable(R.drawable.bg_product_inactive) + btnConfirmPayment.visibility = View.GONE } } } - private fun formatDate(dateString: String): String { + private fun formatDueDate(dateString: String, dueDay: Int): String { return try { 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 outputFormat = SimpleDateFormat("dd MMM; HH.mm", Locale("id", "ID")) val date = inputFormat.parse(dateString) date?.let { val calendar = Calendar.getInstance() calendar.time = it - calendar.set(Calendar.HOUR_OF_DAY, 23) - calendar.set(Calendar.MINUTE, 59) + calendar.add(Calendar.DATE, dueDay) outputFormat.format(calendar.time) } ?: dateString @@ -316,27 +309,10 @@ class SellsAdapter( } } - private fun formatDatePay(dateString: String): String { - return try { - // Parse the ISO 8601 date - val isoDateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault()) - isoDateFormat.timeZone = TimeZone.getTimeZone("UTC") - - val createdDate = isoDateFormat.parse(dateString) - - // Add 24 hours to get due date - val calendar = Calendar.getInstance() - calendar.time = createdDate - calendar.add(Calendar.HOUR, 24) - - // Format due date for display - val dueDateFormat = SimpleDateFormat("dd MMM yyyy", Locale.getDefault()) - dueDateFormat.format(calendar.time) - - } catch (e: Exception) { - Log.e("DateFormatting", "Error formatting date: ${e.message}") - dateString - } + private fun formatPrice(price: String): String { + val priceDouble = price.toDoubleOrNull() ?: 0.0 + val formattedPrice = String.format(Locale("id", "ID"), "Rp%,.0f", priceDouble) + return formattedPrice } } } \ No newline at end of file diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsFragment.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsFragment.kt index 5074a2c..2d5d2ac 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsFragment.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsFragment.kt @@ -42,14 +42,14 @@ class SellsFragment : Fragment() { // Connect TabLayout with ViewPager2 TabLayoutMediator(binding.tabLayoutSells, binding.viewPagerSells) { tab, position -> tab.text = when (position) { - 0 -> getString(R.string.all_orders) // "Semua Pesanan" - 1 -> getString(R.string.pending_orders) // "Menunggu Tagihan" - 2 -> getString(R.string.unpaid_orders) // "Konfirmasi Bayar" - 3 -> getString(R.string.paid_orders) // "Diproses" - 4 -> getString(R.string.processed_orders) // "Sudah Dibayar" - 5 -> getString(R.string.shipped_orders) // "Dikirim" - 6 -> getString(R.string.completed_orders) // "Selesai" - 7 -> getString(R.string.canceled_orders) // "Dibatalkan" + 0 -> getString(R.string.all_sells) + 1 -> getString(R.string.unpaid_sells) + 2 -> getString(R.string.paid_sells) + 3 -> getString(R.string.processed_sells) + 4 -> getString(R.string.shipped_sells) + 5 -> getString(R.string.delivered_sells) + 6 -> getString(R.string.completed_sells) + 7 -> getString(R.string.canceled_sells) else -> "Tab $position" } }.attach() diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsListFragment.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsListFragment.kt index ed01095..a2ae776 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsListFragment.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsListFragment.kt @@ -33,12 +33,10 @@ class SellsListFragment : Fragment() { } } private lateinit var sellsAdapter: SellsAdapter - private var status: String = "all" companion object { private const val TAG = "SellsListFragment" - private const val ARG_STATUS = "status" fun newInstance(status: String): SellsListFragment { @@ -123,7 +121,7 @@ class SellsListFragment : Fragment() { Log.d(TAG, "✅ Data is available: ${result.data.size} items") binding.tvEmptyState.visibility = View.GONE binding.rvSells.visibility = View.VISIBLE -// Log first few items for debugging + result.data.take(3).forEachIndexed { index, order -> Log.d(TAG, "Order ${index + 1}: ID=${order.orderId}, Status=${order.status}, Customer=${order.username}") } diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsViewPagerAdapter.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsViewPagerAdapter.kt index 85ead97..90ff3d3 100644 --- a/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsViewPagerAdapter.kt +++ b/app/src/main/java/com/alya/ecommerce_serang/ui/profile/mystore/sells/SellsViewPagerAdapter.kt @@ -10,14 +10,14 @@ class SellsViewPagerAdapter( // Define all possible sells statuses - keeping your original list private val sellsStatuses = listOf( - "all", // Position 0: "Semua Pesanan" - "pending", // Position 1: "Menunggu Tagihan" - "unpaid", // Position 2: "Konfirmasi Bayar" - "paid", // Position 3: "Diproses" - "processed", // Position 4: "Sudah Dibayar" - "shipped", // Position 5: "Dikirim" - "completed", // Position 6: "Selesai" - "canceled" // Position 7: "Dibatalkan" + "all", + "unpaid", + "paid", + "processed", + "shipped", + "delivered", + "completed", + "canceled" ) override fun getItemCount(): Int = sellsStatuses.size diff --git a/app/src/main/res/layout/item_sells.xml b/app/src/main/res/layout/item_sells.xml index 00cf599..08ca0f7 100644 --- a/app/src/main/res/layout/item_sells.xml +++ b/app/src/main/res/layout/item_sells.xml @@ -1,163 +1,873 @@ - + android:orientation="vertical"> + android:background="@color/white" + android:paddingTop="16dp"> - + + app:layout_constraintTop_toTopOf="parent"/> - + + + + + + + + + + + + + + + + + android:orientation="vertical" + android:layout_marginEnd="16dp" + android:gravity="end"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + app:layout_constraintTop_toBottomOf="@id/layout_order_detail"/> - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +