add order

This commit is contained in:
shaulascr
2025-04-08 02:55:02 +07:00
parent b8e6d93b24
commit 65cd210e3f
16 changed files with 811 additions and 21 deletions

1
.idea/misc.xml generated
View File

@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CodeInsightWorkspaceSettings">
<option name="optimizeImportsOnTheFly" value="true" />

View File

@ -23,7 +23,7 @@ android {
buildTypes {
release {
buildConfigField("String", "BASE_URL", "\"http://192.168.1.4:3000/\"")
buildConfigField("String", "BASE_URL", "\"http://192.168.1.5:3000/\"")
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
@ -31,7 +31,7 @@ android {
)
}
debug {
buildConfigField("String", "BASE_URL", "\"http://192.168.1.4:3000/\"")
buildConfigField("String", "BASE_URL", "\"http://192.168.1.5:3000/\"")
}
}
compileOptions {

View File

@ -0,0 +1,12 @@
package com.alya.ecommerce_serang.data.api.dto
data class CheckoutData(
val orderRequest: OrderRequest,
// Additional UI-related data
val productName: String,
val productImageUrl: String,
val productPrice: Double,
val sellerName: String,
val sellerImageUrl: String,
val sellerId: Int
)

View File

@ -0,0 +1,41 @@
package com.alya.ecommerce_serang.data.api.dto
import com.google.gson.annotations.SerializedName
data class CreateAddressRequest (
@SerializedName("latitude")
val lat: Double,
@SerializedName("longitude")
val long: Double,
@SerializedName("street")
val street: String,
@SerializedName("subdistrict")
val subDistrict: String,
@SerializedName("city_id")
val cityId: Int,
@SerializedName("province_id")
val provId: Int,
@SerializedName("postal_code")
val postCode: String,
@SerializedName("detail")
val detailAddress: String? = null,
@SerializedName("user_id")
val userId: Int,
@SerializedName("recipient")
val recipient: String,
@SerializedName("phone")
val phone: String,
@SerializedName("is_store_location")
val isStoreLocation: Boolean
)

View File

@ -0,0 +1,32 @@
package com.alya.ecommerce_serang.data.api.dto
import com.google.gson.annotations.SerializedName
data class OrderRequest (
@SerializedName("address_id")
val addressId : Int,
@SerializedName("payment_method_id")
val paymentMethodId : Int,
@SerializedName("ship_price")
val shipPrice : Int,
@SerializedName("ship_name")
val shipName : String,
@SerializedName("ship_service")
val shipService : String,
@SerializedName("is_negotiable")
val isNego: Boolean,
@SerializedName("product_id")
val productIdItem: Int,
@SerializedName("quantity")
val quantity: Int,
@SerializedName("ship_etd")
val shipEtd: String
)

View File

@ -1,12 +1,15 @@
package com.alya.ecommerce_serang.data.api.retrofit
import com.alya.ecommerce_serang.data.api.dto.LoginRequest
import com.alya.ecommerce_serang.data.api.dto.OrderRequest
import com.alya.ecommerce_serang.data.api.dto.OtpRequest
import com.alya.ecommerce_serang.data.api.dto.RegisterRequest
import com.alya.ecommerce_serang.data.api.response.AddressResponse
import com.alya.ecommerce_serang.data.api.response.AllProductResponse
import com.alya.ecommerce_serang.data.api.response.CategoryResponse
import com.alya.ecommerce_serang.data.api.response.DetailStoreProductResponse
import com.alya.ecommerce_serang.data.api.response.LoginResponse
import com.alya.ecommerce_serang.data.api.response.OrderResponse
import com.alya.ecommerce_serang.data.api.response.OtpResponse
import com.alya.ecommerce_serang.data.api.response.ProductResponse
import com.alya.ecommerce_serang.data.api.response.ProfileResponse
@ -61,6 +64,16 @@ interface ApiService {
@Path("id") storeId: Int
): Response<DetailStoreProductResponse>
@POST("order")
suspend fun postOrder(
@Body request: OrderRequest
): Response<OrderResponse>
@GET("profile/address")
suspend fun getAddress(
@Body addressRequest: AddressRequest
): Response<AddressResponse>
@GET("mystore")
fun getStore (): Call<StoreResponse>

View File

@ -0,0 +1,42 @@
package com.alya.ecommerce_serang.data.repository
import android.util.Log
import com.alya.ecommerce_serang.data.api.dto.OrderRequest
import com.alya.ecommerce_serang.data.api.response.OrderResponse
import com.alya.ecommerce_serang.data.api.response.ProductResponse
import com.alya.ecommerce_serang.data.api.retrofit.ApiService
import retrofit2.Response
class OrderRepository(private val apiService: ApiService) {
suspend fun fetchProductDetail(productId: Int): ProductResponse? {
return try {
val response = apiService.getDetailProduct(productId)
if (response.isSuccessful) {
response.body()
} else {
Log.e("ProductRepository", "Error: ${response.errorBody()?.string()}")
null
}
} catch (e: Exception) {
null
}
}
suspend fun createOrder(orderRequest: OrderRequest): Response<OrderResponse> {
return apiService.postOrder(orderRequest)
}
//not yet implement the api service address
// suspend fun getAddressDetails(addressId: Int): AddressesItem {
// // Simulate API call to get address details
// kotlinx.coroutines.delay(300) // Simulate network request
// // Return mock data
// return AddressesItem(
// id = addressId,
// label = "Rumah",
// fullAddress = "Jl. Pegangasan Timur No. 42, Jakarta"
// )
// }
}

View File

@ -1,21 +1,392 @@
package com.alya.ecommerce_serang.ui.order
import android.app.Activity
import android.app.ProgressDialog
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import android.os.Looper
import android.util.Log
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.alya.ecommerce_serang.R
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.alya.ecommerce_serang.data.api.dto.CheckoutData
import com.alya.ecommerce_serang.data.api.dto.OrderRequest
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
import com.alya.ecommerce_serang.data.api.retrofit.ApiService
import com.alya.ecommerce_serang.data.repository.OrderRepository
import com.alya.ecommerce_serang.data.repository.ProductRepository
import com.alya.ecommerce_serang.databinding.ActivityCheckoutBinding
import com.alya.ecommerce_serang.databinding.ActivityDetailProductBinding
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
import com.alya.ecommerce_serang.utils.SessionManager
import com.google.gson.Gson
import java.util.Locale
class CheckoutActivity : AppCompatActivity() {
private lateinit var binding: ActivityCheckoutBinding
private lateinit var apiService: ApiService
private lateinit var sessionManager: SessionManager
private var itemOrderAdapter: CheckoutSellerAdapter? = null
private val viewModel: CheckoutViewModel by viewModels {
BaseViewModelFactory {
val apiService = ApiConfig.getApiService(sessionManager)
val productRepository = ProductRepository(apiService)
val orderRepository = OrderRepository(apiService)
CheckoutViewModel(orderRepository)
}
}
private var orderRequest: OrderRequest? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_checkout)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
binding = ActivityCheckoutBinding.inflate(layoutInflater)
setContentView(binding.root)
sessionManager = SessionManager(this)
apiService = ApiConfig.getApiService(sessionManager)
// Get order request from intent
getOrderRequestFromIntent()
// Setup UI components
setupToolbar()
setupObservers()
setupClickListeners()
// Load data if order request is available
orderRequest?.let {
viewModel.loadCheckoutData(it)
// Update shipping method display
binding.tvShippingMethod.text = "${it.shipName} ${it.shipService} (${it.shipEtd} hari)"
} ?: run {
// Handle case when order request is not available
Toast.makeText(this, "Error: Order request data not found", Toast.LENGTH_SHORT).show()
finish()
}
}
private fun getOrderRequestFromIntent() {
// Check for direct OrderRequest object
if (intent.hasExtra(EXTRA_ORDER_REQUEST)) {
orderRequest = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getSerializableExtra(EXTRA_ORDER_REQUEST, OrderRequest::class.java)
} else {
@Suppress("DEPRECATION")
intent.getSerializableExtra(EXTRA_ORDER_REQUEST) as? OrderRequest
}
}
// Check for JSON string
else if (intent.hasExtra(EXTRA_ORDER_REQUEST_JSON)) {
val jsonString = intent.getStringExtra(EXTRA_ORDER_REQUEST_JSON)
try {
orderRequest = Gson().fromJson(jsonString, OrderRequest::class.java)
} catch (e: Exception) {
Log.e(TAG, "Error parsing order request JSON", e)
}
}
// Check for individual fields
else if (intent.hasExtra(EXTRA_ADDRESS_ID) && intent.hasExtra(EXTRA_PRODUCT_ID)) {
orderRequest = OrderRequest(
address_id = intent.getIntExtra(EXTRA_ADDRESS_ID, 0),
payment_method_id = intent.getIntExtra(EXTRA_PAYMENT_METHOD_ID, 0),
ship_price = intent.getIntExtra(EXTRA_SHIP_PRICE, 0),
ship_name = intent.getStringExtra(EXTRA_SHIP_NAME) ?: "",
ship_service = intent.getStringExtra(EXTRA_SHIP_SERVICE) ?: "",
is_negotiable = intent.getBooleanExtra(EXTRA_IS_NEGOTIABLE, false),
product_id = intent.getIntExtra(EXTRA_PRODUCT_ID, 0),
quantity = intent.getIntExtra(EXTRA_QUANTITY, 0),
ship_etd = intent.getStringExtra(EXTRA_SHIP_ETD) ?: ""
)
}
}
private fun setupToolbar() {
binding.toolbar.setNavigationOnClickListener {
finish()
}
}
private fun setupObservers() {
// Observe checkout data
viewModel.checkoutData.observe(this) { data ->
setupSellerOrderRecyclerView(data)
updateOrderSummary()
}
// Observe address details
viewModel.addressDetails.observe(this) { address ->
binding.tvPlacesAddress.text = address.label
binding.tvAddress.text = address.fullAddress
}
// Observe payment details
viewModel.paymentDetails.observe(this) { payment ->
binding.tvPaymentMethod.text = payment.name
}
// Observe loading state
viewModel.isLoading.observe(this) { isLoading ->
// Show/hide loading indicator
// binding.progressBar.isVisible = isLoading
}
}
private fun setupSellerOrderRecyclerView(checkoutData: CheckoutData) {
val adapter = CheckoutSellerAdapter(checkoutData)
binding.rvSellerOrder.apply {
layoutManager = LinearLayoutManager(this@CheckoutActivity)
this.adapter = adapter
isNestedScrollingEnabled = false
}
}
private fun updateOrderSummary() {
viewModel.checkoutData.value?.let { data ->
// Calculate subtotal (product price * quantity)
val subtotal = data.productPrice * data.orderRequest.quantity
binding.tvSubtotal.text = formatCurrency(subtotal)
// Calculate total (subtotal + shipping)
val total = subtotal + data.orderRequest.ship_price
binding.tvTotal.text = formatCurrency(total)
}
}
private fun setupClickListeners() {
// Setup address selection
binding.tvChangeAddress.setOnClickListener {
// Launch address selection activity
startActivityForResult(
Intent(this, AddressSelectionActivity::class.java),
REQUEST_ADDRESS
)
}
// Setup payment button
binding.btnPay.setOnClickListener {
// Create the order by sending API request
if (validateOrder()) {
createOrder()
}
}
// Setup voucher section
binding.layoutVoucher.setOnClickListener {
Toast.makeText(this, "Select Voucher", Toast.LENGTH_SHORT).show()
}
// Setup shipping method
binding.layoutShippingMethod.setOnClickListener {
// Launch shipping method selection
val orderRequest = this.orderRequest ?: return@setOnClickListener
val intent = Intent(this, ShippingMethodActivity::class.java)
intent.putExtra(ShippingMethodActivity.EXTRA_PRODUCT_ID, orderRequest.product_id)
startActivityForResult(intent, REQUEST_SHIPPING)
}
// Setup payment method
binding.layoutPaymentMethod.setOnClickListener {
// Launch payment method selection
startActivityForResult(
Intent(this, PaymentMethodActivity::class.java),
REQUEST_PAYMENT
)
}
}
private fun formatCurrency(amount: Double): String {
val formatter = NumberFormat.getCurrencyInstance(Locale("in", "ID"))
return formatter.format(amount).replace(",00", "")
}
private fun validateOrder(): Boolean {
val orderRequest = this.orderRequest ?: return false
// Check address
if (orderRequest.address_id <= 0) {
Toast.makeText(this, "Silakan pilih alamat pengiriman", Toast.LENGTH_SHORT).show()
return false
}
// Check shipping method
if (orderRequest.ship_name.isEmpty() || orderRequest.ship_service.isEmpty()) {
Toast.makeText(this, "Silakan pilih metode pengiriman", Toast.LENGTH_SHORT).show()
return false
}
// Check payment method
if (orderRequest.payment_method_id <= 0) {
Toast.makeText(this, "Silakan pilih metode pembayaran", Toast.LENGTH_SHORT).show()
return false
}
return true
}
private fun createOrder() {
val orderRequest = this.orderRequest ?: return
// Show progress dialog
val progressDialog = ProgressDialog(this)
progressDialog.setMessage("Membuat pesanan...")
progressDialog.setCancelable(false)
progressDialog.show()
// In a real app, you would send the order request to your API
// For now, we'll simulate an API call
Handler(Looper.getMainLooper()).postDelayed({
progressDialog.dismiss()
// Show success message
Toast.makeText(this, "Pesanan berhasil dibuat!", Toast.LENGTH_SHORT).show()
// Create intent result with the order request
val resultIntent = Intent()
resultIntent.putExtra(EXTRA_ORDER_REQUEST, orderRequest)
setResult(RESULT_OK, resultIntent)
// Return to previous screen
finish()
}, 1500)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK) {
when (requestCode) {
REQUEST_ADDRESS -> {
// Handle address selection result
val addressId = data?.getIntExtra(AddressSelectionActivity.EXTRA_ADDRESS_ID, 0) ?: 0
if (addressId > 0) {
orderRequest?.address_id = addressId
// Reload address details
orderRequest?.let { request ->
viewModelScope.launch {
val addressDetails = repository.getAddressDetails(request.address_id)
binding.tvPlacesAddress.text = addressDetails.label
binding.tvAddress.text = addressDetails.fullAddress
}
}
}
}
REQUEST_SHIPPING -> {
// Handle shipping method selection result
data?.let { intent ->
val shipName = intent.getStringExtra(ShippingMethodActivity.EXTRA_SHIP_NAME) ?: return
val shipService = intent.getStringExtra(ShippingMethodActivity.EXTRA_SHIP_SERVICE) ?: return
val shipPrice = intent.getIntExtra(ShippingMethodActivity.EXTRA_SHIP_PRICE, 0)
val shipEtd = intent.getStringExtra(ShippingMethodActivity.EXTRA_SHIP_ETD) ?: ""
// Update order request
orderRequest?.apply {
this.ship_name = shipName
this.ship_service = shipService
this.ship_price = shipPrice
this.ship_etd = shipEtd
}
// Update UI
binding.tvShippingMethod.text = "$shipName $shipService ($shipEtd hari)"
updateOrderSummary()
}
}
REQUEST_PAYMENT -> {
// Handle payment method selection result
val paymentMethodId = data?.getIntExtra(PaymentMethodActivity.EXTRA_PAYMENT_METHOD_ID, 0) ?: 0
if (paymentMethodId > 0) {
orderRequest?.payment_method_id = paymentMethodId
// Reload payment method details
orderRequest?.let { request ->
viewModelScope.launch {
val paymentDetails = repository.getPaymentMethodDetails(request.payment_method_id)
binding.tvPaymentMethod.text = paymentDetails.name
}
}
}
}
}
}
}
companion object {
private const val TAG = "CheckoutActivity"
// Request codes
const val REQUEST_ADDRESS = 100
const val REQUEST_SHIPPING = 101
const val REQUEST_PAYMENT = 102
// Intent extras
const val EXTRA_ORDER_REQUEST = "extra_order_request"
const val EXTRA_ORDER_REQUEST_JSON = "extra_order_request_json"
// Individual field extras
const val EXTRA_ADDRESS_ID = "extra_address_id"
const val EXTRA_PAYMENT_METHOD_ID = "extra_payment_method_id"
const val EXTRA_SHIP_PRICE = "extra_ship_price"
const val EXTRA_SHIP_NAME = "extra_ship_name"
const val EXTRA_SHIP_SERVICE = "extra_ship_service"
const val EXTRA_IS_NEGOTIABLE = "extra_is_negotiable"
const val EXTRA_PRODUCT_ID = "extra_product_id"
const val EXTRA_QUANTITY = "extra_quantity"
const val EXTRA_SHIP_ETD = "extra_ship_etd"
// Start methods for various ways to launch the activity
// Start with OrderRequest object
fun start(context: Context, orderRequest: OrderRequest) {
val intent = Intent(context, CheckoutActivity::class.java)
intent.putExtra(EXTRA_ORDER_REQUEST, orderRequest)
context.startActivity(intent)
}
// Start with OrderRequest JSON
fun startWithJson(context: Context, orderRequestJson: String) {
val intent = Intent(context, CheckoutActivity::class.java)
intent.putExtra(EXTRA_ORDER_REQUEST_JSON, orderRequestJson)
context.startActivity(intent)
}
// Start with individual fields
fun start(
context: Context,
addressId: Int,
paymentMethodId: Int,
shipPrice: Int,
shipName: String,
shipService: String,
isNegotiable: Boolean,
productId: Int,
quantity: Int,
shipEtd: String
) {
val intent = Intent(context, CheckoutActivity::class.java).apply {
putExtra(EXTRA_ADDRESS_ID, addressId)
putExtra(EXTRA_PAYMENT_METHOD_ID, paymentMethodId)
putExtra(EXTRA_SHIP_PRICE, shipPrice)
putExtra(EXTRA_SHIP_NAME, shipName)
putExtra(EXTRA_SHIP_SERVICE, shipService)
putExtra(EXTRA_IS_NEGOTIABLE, isNegotiable)
putExtra(EXTRA_PRODUCT_ID, productId)
putExtra(EXTRA_QUANTITY, quantity)
putExtra(EXTRA_SHIP_ETD, shipEtd)
}
context.startActivity(intent)
}
// Launch for result with OrderRequest
fun startForResult(activity: Activity, orderRequest: OrderRequest, requestCode: Int) {
val intent = Intent(activity, CheckoutActivity::class.java)
intent.putExtra(EXTRA_ORDER_REQUEST, orderRequest)
activity.startActivityForResult(intent, requestCode)
}
}
}

View File

@ -0,0 +1,47 @@
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.R
import com.alya.ecommerce_serang.data.api.dto.CheckoutData
import com.alya.ecommerce_serang.databinding.ItemOrderProductBinding
import com.bumptech.glide.Glide
import java.text.NumberFormat
import java.util.Locale
class CheckoutProductAdapter(private val checkoutData: CheckoutData) :
RecyclerView.Adapter<CheckoutProductAdapter.ProductViewHolder>() {
class ProductViewHolder(val binding: ItemOrderProductBinding) : RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductViewHolder {
val binding = ItemOrderProductBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return ProductViewHolder(binding)
}
override fun getItemCount(): Int = 1 // Only one product based on your JSON
override fun onBindViewHolder(holder: ProductViewHolder, position: Int) {
with(holder.binding) {
tvProductName.text = checkoutData.productName
tvProductQuantity.text = "${checkoutData.orderRequest.quantity} buah"
tvProductPrice.text = formatCurrency(checkoutData.productPrice)
// Load image with Glide
Glide.with(ivProduct.context)
.load(checkoutData.productImageUrl)
.placeholder(R.drawable.placeholder_image)
.into(ivProduct)
}
}
private fun formatCurrency(amount: Double): String {
val formatter = NumberFormat.getCurrencyInstance(Locale("in", "ID"))
return formatter.format(amount).replace(",00", "")
}
}

View File

@ -0,0 +1,47 @@
package com.alya.ecommerce_serang.ui.order
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.alya.ecommerce_serang.R
import com.alya.ecommerce_serang.data.api.dto.CheckoutData
import com.alya.ecommerce_serang.databinding.ItemOrderSellerBinding
import com.bumptech.glide.Glide
// Adapter for seller section that contains the product
class CheckoutSellerAdapter(private val checkoutData: CheckoutData) :
RecyclerView.Adapter<CheckoutSellerAdapter.SellerViewHolder>() {
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)
}
override fun getItemCount(): Int = 1 // Only one seller based on your JSON
override fun onBindViewHolder(holder: SellerViewHolder, position: Int) {
with(holder.binding) {
tvListProductOrder.text = checkoutData.sellerName
// Load seller image
Glide.with(ivSellerOrder.context)
.load(checkoutData.sellerImageUrl)
.placeholder(R.drawable.placeholder_image)
.into(ivSellerOrder)
// Set up nested RecyclerView for the product
rvSellerOrderProduct.apply {
layoutManager = LinearLayoutManager(context)
adapter = CheckoutProductAdapter(checkoutData)
isNestedScrollingEnabled = false
}
}
}
}

View File

@ -0,0 +1,62 @@
package com.alya.ecommerce_serang.ui.order
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.alya.ecommerce_serang.data.api.dto.CheckoutData
import com.alya.ecommerce_serang.data.api.dto.OrderRequest
import com.alya.ecommerce_serang.data.api.response.AddressesItem
import com.alya.ecommerce_serang.data.api.response.OrderResponse
import com.alya.ecommerce_serang.data.repository.OrderRepository
import com.alya.ecommerce_serang.data.repository.Result
import kotlinx.coroutines.launch
class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
private val _checkoutData = MutableLiveData<CheckoutData>()
val checkoutData: LiveData<CheckoutData> = _checkoutData
private val _addressDetails = MutableLiveData<AddressesItem>()
val addressDetails: LiveData<AddressesItem> = _addressDetails
private val _isLoading = MutableLiveData<Boolean>()
val isLoading: LiveData<Boolean> = _isLoading
fun loadCheckoutData(orderRequest: OrderRequest) {
viewModelScope.launch {
_isLoading.value = true
try {
// Load all necessary data
val productDetails = repository.fetchProductDetail(orderRequest.productIdItem)
// val addressDetails = repository.getAddressDetails(orderRequest.address_id)
// Update LiveData objects
// _addressDetails.value = addressDetails
// Create CheckoutData object
_checkoutData.value = CheckoutData(
orderRequest = orderRequest,
productName = productDetails?.product?.productName,
productImageUrl = productDetails.product.image,
productPrice = productDetails.product.price,
sellerName = productDetails.product.storeId
// sellerImageUrl = productDetails.sellerImageUrl,
// sellerId = productDetails.sellerId
)
} catch (e: Exception) {
// Handle errors
Log.e("CheckoutViewModel", "Error loading checkout data", e)
} finally {
_isLoading.value = false
}
}
}
fun calculateTotal(): Double {
val data = checkoutData.value ?: return 0.0
return (data.productPrice * data.orderRequest.quantity) + data.orderRequest.shipPrice
}
}

View File

@ -1,4 +0,0 @@
package com.alya.ecommerce_serang.ui.order
class ProductOrderAdapter {
}

View File

@ -1,4 +0,0 @@
package com.alya.ecommerce_serang.ui.order
class SellerOrderAdapter {
}

View File

@ -3,6 +3,9 @@ package com.alya.ecommerce_serang.ui.product
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.ImageButton
import android.widget.TextView
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
@ -16,9 +19,11 @@ import com.alya.ecommerce_serang.data.api.retrofit.ApiService
import com.alya.ecommerce_serang.data.repository.ProductRepository
import com.alya.ecommerce_serang.databinding.ActivityDetailProductBinding
import com.alya.ecommerce_serang.ui.home.HorizontalProductAdapter
import com.alya.ecommerce_serang.ui.order.CheckoutActivity
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
import com.alya.ecommerce_serang.utils.SessionManager
import com.bumptech.glide.Glide
import com.google.android.material.bottomsheet.BottomSheetDialog
class DetailProductActivity : AppCompatActivity() {
private lateinit var binding: ActivityDetailProductBinding
@ -95,6 +100,13 @@ class DetailProductActivity : AppCompatActivity() {
handleAllReviewsClick(product.productId)
}
binding.btnBuyNow.setOnClickListener {
viewModel.productDetail.value?.productId?.let { id ->
showBuyNowPopup(id)
}
}
val fullImageUrl = when (val img = product.image) {
is String -> {
if (img.startsWith("/")) BASE_URL + img.substring(1) else img
@ -155,4 +167,45 @@ class DetailProductActivity : AppCompatActivity() {
intent.putExtra("PRODUCT_ID", product.id) // Pass product ID
startActivity(intent)
}
private fun showBuyNowPopup(productId: Int) {
val bottomSheetDialog = BottomSheetDialog(this)
val view = layoutInflater.inflate(R.layout.dialog_count_buy, null)
bottomSheetDialog.setContentView(view)
val btnDecrease = view.findViewById<Button>(R.id.btnDecrease)
val btnIncrease = view.findViewById<Button>(R.id.btnIncrease)
val tvQuantity = view.findViewById<TextView>(R.id.tvQuantity)
val btnBuyNow = view.findViewById<Button>(R.id.btnBuyNow)
val btnClose = view.findViewById<ImageButton>(R.id.btnCloseDialog)
var quantity = 1
tvQuantity.text = quantity.toString()
btnDecrease.setOnClickListener {
if (quantity > 1) {
quantity--
tvQuantity.text = quantity.toString()
}
}
btnIncrease.setOnClickListener {
quantity++
tvQuantity.text = quantity.toString()
}
btnBuyNow.setOnClickListener {
bottomSheetDialog.dismiss()
val intent = Intent(this, CheckoutActivity::class.java)
intent.putExtra("PRODUCT_ID", productId)
intent.putExtra("QUANTITY", quantity)
startActivity(intent)
}
btnClose.setOnClickListener {
bottomSheetDialog.dismiss()
}
bottomSheetDialog.show()
}
}

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/white" />
<corners android:radius="16dp"/>
<padding android:left="16dp" android:top="16dp"
android:right="16dp" android:bottom="16dp"/>
</shape>

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_popup_count"
android:padding="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center_horizontal"
android:paddingTop="32dp">
<TextView
android:id="@+id/tvQuantityTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Jumlah Produk"
android:textSize="18sp"
android:textStyle="bold"
android:paddingBottom="16dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<Button
android:id="@+id/btnDecrease"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="-" />
<TextView
android:id="@+id/tvQuantity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:text="1"
android:textSize="18sp" />
<Button
android:id="@+id/btnIncrease"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="+" />
</LinearLayout>
<Button
android:id="@+id/btnBuyNow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Beli Sekarang"
android:layout_marginTop="24dp"
android:backgroundTint="@color/blue_500"
android:textColor="@android:color/white" />
</LinearLayout>
<!-- Tombol X Close -->
<ImageButton
android:id="@+id/btnCloseDialog"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="end|top"
android:background="@android:color/transparent"
android:src="@android:drawable/ic_menu_close_clear_cancel"
android:contentDescription="Close" />
</FrameLayout>