mirror of
https://github.com/shaulascr/ecommerce_serang.git
synced 2025-08-13 10:42: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">
|
<project version="4">
|
||||||
<component name="CodeInsightWorkspaceSettings">
|
<component name="CodeInsightWorkspaceSettings">
|
||||||
<option name="optimizeImportsOnTheFly" value="true" />
|
<option name="optimizeImportsOnTheFly" value="true" />
|
||||||
|
@ -23,7 +23,7 @@ android {
|
|||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
buildConfigField("String", "BASE_URL", "\"http://192.168.1.4:3000/\"")
|
buildConfigField("String", "BASE_URL", "\"http://192.168.1.5:3000/\"")
|
||||||
isMinifyEnabled = false
|
isMinifyEnabled = false
|
||||||
proguardFiles(
|
proguardFiles(
|
||||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
@ -31,7 +31,7 @@ android {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
debug {
|
debug {
|
||||||
buildConfigField("String", "BASE_URL", "\"http://192.168.1.4:3000/\"")
|
buildConfigField("String", "BASE_URL", "\"http://192.168.1.5:3000/\"")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
compileOptions {
|
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
|
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.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.OtpRequest
|
||||||
import com.alya.ecommerce_serang.data.api.dto.RegisterRequest
|
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.AllProductResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.CategoryResponse
|
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.DetailStoreProductResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.LoginResponse
|
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.OtpResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.ProductResponse
|
import com.alya.ecommerce_serang.data.api.response.ProductResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.ProfileResponse
|
import com.alya.ecommerce_serang.data.api.response.ProfileResponse
|
||||||
@ -61,6 +64,16 @@ interface ApiService {
|
|||||||
@Path("id") storeId: Int
|
@Path("id") storeId: Int
|
||||||
): Response<DetailStoreProductResponse>
|
): 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")
|
@GET("mystore")
|
||||||
fun getStore (): Call<StoreResponse>
|
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
|
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 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.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.alya.ecommerce_serang.R
|
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() {
|
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?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
enableEdgeToEdge()
|
binding = ActivityCheckoutBinding.inflate(layoutInflater)
|
||||||
setContentView(R.layout.activity_checkout)
|
setContentView(binding.root)
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
|
|
||||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
sessionManager = SessionManager(this)
|
||||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
apiService = ApiConfig.getApiService(sessionManager)
|
||||||
insets
|
|
||||||
|
// 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.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.widget.Button
|
||||||
|
import android.widget.ImageButton
|
||||||
|
import android.widget.TextView
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
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.data.repository.ProductRepository
|
||||||
import com.alya.ecommerce_serang.databinding.ActivityDetailProductBinding
|
import com.alya.ecommerce_serang.databinding.ActivityDetailProductBinding
|
||||||
import com.alya.ecommerce_serang.ui.home.HorizontalProductAdapter
|
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.BaseViewModelFactory
|
||||||
import com.alya.ecommerce_serang.utils.SessionManager
|
import com.alya.ecommerce_serang.utils.SessionManager
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
|
|
||||||
class DetailProductActivity : AppCompatActivity() {
|
class DetailProductActivity : AppCompatActivity() {
|
||||||
private lateinit var binding: ActivityDetailProductBinding
|
private lateinit var binding: ActivityDetailProductBinding
|
||||||
@ -95,6 +100,13 @@ class DetailProductActivity : AppCompatActivity() {
|
|||||||
handleAllReviewsClick(product.productId)
|
handleAllReviewsClick(product.productId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binding.btnBuyNow.setOnClickListener {
|
||||||
|
viewModel.productDetail.value?.productId?.let { id ->
|
||||||
|
showBuyNowPopup(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
val fullImageUrl = when (val img = product.image) {
|
val fullImageUrl = when (val img = product.image) {
|
||||||
is String -> {
|
is String -> {
|
||||||
if (img.startsWith("/")) BASE_URL + img.substring(1) else img
|
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
|
intent.putExtra("PRODUCT_ID", product.id) // Pass product ID
|
||||||
startActivity(intent)
|
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