mirror of
https://github.com/shaulascr/ecommerce_serang.git
synced 2025-08-10 09:22:21 +00:00
add order
This commit is contained in:
1
.idea/misc.xml
generated
1
.idea/misc.xml
generated
@ -1,4 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CodeInsightWorkspaceSettings">
|
||||
<option name="optimizeImportsOnTheFly" value="true" />
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
)
|
@ -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
|
||||
|
||||
)
|
@ -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
|
||||
)
|
@ -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>
|
||||
|
@ -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"
|
||||
// )
|
||||
// }
|
||||
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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", "")
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
package com.alya.ecommerce_serang.ui.order
|
||||
|
||||
class ProductOrderAdapter {
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
package com.alya.ecommerce_serang.ui.order
|
||||
|
||||
class SellerOrderAdapter {
|
||||
}
|
@ -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()
|
||||
}
|
||||
|
||||
}
|
8
app/src/main/res/drawable/bg_popup_count.xml
Normal file
8
app/src/main/res/drawable/bg_popup_count.xml
Normal 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>
|
71
app/src/main/res/layout/dialog_count_buy.xml
Normal file
71
app/src/main/res/layout/dialog_count_buy.xml
Normal 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>
|
||||
|
Reference in New Issue
Block a user