mirror of
https://github.com/shaulascr/ecommerce_serang.git
synced 2025-08-10 09:22:21 +00:00
add cancel and logout
This commit is contained in:
@ -1,50 +1,17 @@
|
||||
package com.alya.ecommerce_serang.app
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import com.google.firebase.FirebaseApp
|
||||
import com.google.firebase.messaging.FirebaseMessaging
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
|
||||
@HiltAndroidApp
|
||||
class App : Application(){
|
||||
private val TAG = "AppSerang"
|
||||
// private val TAG = "AppSerang"
|
||||
//
|
||||
//// var tokenTes: String? = null
|
||||
//
|
||||
// override fun onCreate() {
|
||||
//
|
||||
// }
|
||||
|
||||
// var tokenTes: String? = null
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
// Initialize Firebase
|
||||
FirebaseApp.initializeApp(this)
|
||||
|
||||
// Request FCM token at app startup
|
||||
retrieveFCMToken()
|
||||
}
|
||||
|
||||
private fun retrieveFCMToken() {
|
||||
FirebaseMessaging.getInstance().token
|
||||
.addOnCompleteListener { task ->
|
||||
if (!task.isSuccessful) {
|
||||
Log.e(TAG, "Failed to get FCM token", task.exception)
|
||||
return@addOnCompleteListener
|
||||
}
|
||||
|
||||
val token = task.result
|
||||
// tokenTes = token
|
||||
Log.d(TAG, "FCM token retrieved: $token")
|
||||
|
||||
// Save token locally
|
||||
val sharedPreferences = getSharedPreferences("FCM_PREFS", Context.MODE_PRIVATE)
|
||||
sharedPreferences.edit().putString("FCM_TOKEN", token).apply()
|
||||
|
||||
// Send to your server
|
||||
sendTokenToServer(token)
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendTokenToServer(token: String) {
|
||||
Log.d(TAG, "Would send token to server: $token")
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.alya.ecommerce_serang.data.api.dto
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class CancelOrderReq (
|
||||
@SerializedName("order_id")
|
||||
val orderId: Int,
|
||||
|
||||
@SerializedName("reason")
|
||||
val reason: String
|
||||
)
|
@ -0,0 +1,14 @@
|
||||
package com.alya.ecommerce_serang.data.api.response.customer.order
|
||||
|
||||
data class CancelOrderResponse(
|
||||
val data: DataCancel,
|
||||
val message: String
|
||||
)
|
||||
|
||||
data class DataCancel(
|
||||
val reason: String,
|
||||
val createdAt: String,
|
||||
val id: Int,
|
||||
val orderId: Int
|
||||
)
|
||||
|
@ -22,6 +22,9 @@ class ApiConfig {
|
||||
val client = OkHttpClient.Builder()
|
||||
.addInterceptor(loggingInterceptor)
|
||||
.addInterceptor(authInterceptor)
|
||||
.connectTimeout(60, TimeUnit.SECONDS) // Increase to 60 seconds
|
||||
.readTimeout(60, TimeUnit.SECONDS) // Increase to 60 seconds
|
||||
.writeTimeout(60, TimeUnit.SECONDS)
|
||||
.build()
|
||||
|
||||
val retrofit = Retrofit.Builder()
|
||||
|
@ -3,6 +3,7 @@ package com.alya.ecommerce_serang.data.api.retrofit
|
||||
|
||||
import com.alya.ecommerce_serang.data.api.dto.AddEvidenceRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.AddPaymentInfoResponse
|
||||
import com.alya.ecommerce_serang.data.api.dto.CancelOrderReq
|
||||
import com.alya.ecommerce_serang.data.api.dto.CartItem
|
||||
import com.alya.ecommerce_serang.data.api.dto.CityResponse
|
||||
import com.alya.ecommerce_serang.data.api.dto.CompletedOrderRequest
|
||||
@ -35,6 +36,7 @@ import com.alya.ecommerce_serang.data.api.response.customer.cart.AddCartResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.cart.DeleteCartResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.cart.ListCartResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.cart.UpdateCartResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.CancelOrderResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.CourierCostResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.CreateOrderResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.CreateReviewResponse
|
||||
@ -163,6 +165,11 @@ interface ApiService {
|
||||
@Body request: OrderRequest
|
||||
): Response<CreateOrderResponse>
|
||||
|
||||
@POST("order/cancel")
|
||||
suspend fun cancelOrder(
|
||||
@Body cancelReq: CancelOrderReq
|
||||
): Response<CancelOrderResponse>
|
||||
|
||||
@GET("order/detail/{id}")
|
||||
suspend fun getDetailOrder(
|
||||
@Path("id") orderId: Int
|
||||
|
@ -2,16 +2,17 @@ package com.alya.ecommerce_serang.data.repository
|
||||
|
||||
import android.util.Log
|
||||
import com.alya.ecommerce_serang.data.api.dto.AddEvidenceMultipartRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.CancelOrderReq
|
||||
import com.alya.ecommerce_serang.data.api.dto.CompletedOrderRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.CourierCostRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.CreateAddressRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.OrderRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.OrderRequestBuy
|
||||
import com.alya.ecommerce_serang.data.api.dto.OrdersItem
|
||||
import com.alya.ecommerce_serang.data.api.dto.ReviewProductItem
|
||||
import com.alya.ecommerce_serang.data.api.dto.UpdateCart
|
||||
import com.alya.ecommerce_serang.data.api.dto.UserProfile
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.cart.DataItemCart
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.CancelOrderResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.CourierCostResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.CreateOrderResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.CreateReviewResponse
|
||||
@ -491,4 +492,23 @@ class OrderRepository(private val apiService: ApiService) {
|
||||
|
||||
}
|
||||
|
||||
suspend fun cancelOrder(cancelReq: CancelOrderReq): Result<CancelOrderResponse>{
|
||||
return try{
|
||||
val response= apiService.cancelOrder(cancelReq)
|
||||
|
||||
if (response.isSuccessful){
|
||||
response.body()?.let { cancelOrderResponse ->
|
||||
Result.Success(cancelOrderResponse)
|
||||
} ?: run {
|
||||
Result.Error(Exception("Failed to cancel order"))
|
||||
}
|
||||
} else {
|
||||
val errorMsg = response.errorBody()?.string() ?: "Unknown Error"
|
||||
Result.Error(Exception(errorMsg))
|
||||
}
|
||||
}catch (e: Exception){
|
||||
Result.Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
package com.alya.ecommerce_serang.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
@ -20,11 +22,15 @@ import com.alya.ecommerce_serang.data.api.retrofit.ApiService
|
||||
import com.alya.ecommerce_serang.databinding.ActivityMainBinding
|
||||
import com.alya.ecommerce_serang.ui.notif.WebSocketManager
|
||||
import com.alya.ecommerce_serang.utils.SessionManager
|
||||
import com.google.firebase.FirebaseApp
|
||||
import com.google.firebase.messaging.FirebaseMessaging
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : AppCompatActivity() {
|
||||
private val TAG = "MainActivity"
|
||||
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
private lateinit var apiService: ApiService
|
||||
private lateinit var sessionManager: SessionManager
|
||||
@ -65,6 +71,11 @@ class MainActivity : AppCompatActivity() {
|
||||
)
|
||||
windowInsets
|
||||
}
|
||||
// Initialize Firebase
|
||||
FirebaseApp.initializeApp(this)
|
||||
|
||||
// Request FCM token at app startup
|
||||
retrieveFCMToken()
|
||||
|
||||
requestNotificationPermissionIfNeeded()
|
||||
|
||||
@ -151,4 +162,31 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun retrieveFCMToken() {
|
||||
FirebaseMessaging.getInstance().token
|
||||
.addOnCompleteListener { task ->
|
||||
if (!task.isSuccessful) {
|
||||
Log.e(TAG, "Failed to get FCM token", task.exception)
|
||||
return@addOnCompleteListener
|
||||
}
|
||||
|
||||
val token = task.result
|
||||
// tokenTes = token
|
||||
Log.d(TAG, "FCM token retrieved: $token")
|
||||
|
||||
// Save token locally
|
||||
val sharedPreferences = getSharedPreferences("FCM_PREFS", Context.MODE_PRIVATE)
|
||||
sharedPreferences.edit().putString("FCM_TOKEN", token).apply()
|
||||
|
||||
// Send to your server
|
||||
sendTokenToServer(token)
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendTokenToServer(token: String) {
|
||||
Log.d(TAG, "Would send token to server: $token")
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -508,6 +508,14 @@ class RegisterStoreActivity : AppCompatActivity() {
|
||||
viewModel.subdistrict.value = s.toString()
|
||||
}
|
||||
})
|
||||
|
||||
binding.etBankName.addTextChangedListener(object: TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
viewModel.subdistrict.value = s.toString()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.alya.ecommerce_serang.ui.order
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
@ -9,6 +10,7 @@ import com.alya.ecommerce_serang.data.api.dto.CourierCostRequest
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.CourierCostsItem
|
||||
import com.alya.ecommerce_serang.data.repository.OrderRepository
|
||||
import com.alya.ecommerce_serang.data.repository.Result
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ShippingViewModel(
|
||||
@ -30,12 +32,71 @@ class ShippingViewModel(
|
||||
/**
|
||||
* Load shipping options based on address, product, and quantity
|
||||
*/
|
||||
// fun loadShippingOptions(addressId: Int, productId: Int, quantity: Int) {
|
||||
// _isLoading.value = true
|
||||
// _errorMessage.value = ""
|
||||
//
|
||||
// val costProduct = CostProduct(
|
||||
// productId = productId,
|
||||
// quantity = quantity
|
||||
// )
|
||||
//
|
||||
// viewModelScope.launch {
|
||||
// // Define the courier services to try
|
||||
// val courierServices = listOf("pos", "jne", "tiki")
|
||||
//
|
||||
// // Create a mutable list to collect successful courier options
|
||||
// val availableCourierOptions = mutableListOf<CourierCostsItem>()
|
||||
//
|
||||
// // Try each courier service
|
||||
// for (courier in courierServices) {
|
||||
// try {
|
||||
// // Create a request for this specific courier
|
||||
// val courierRequest = CourierCostRequest(
|
||||
// addressId = addressId,
|
||||
// itemCost = listOf(costProduct),
|
||||
// courier = courier // Add the courier to the request
|
||||
// )
|
||||
//
|
||||
// // Make a separate API call for each courier
|
||||
// val result = repository.getCountCourierCost(courierRequest)
|
||||
//
|
||||
// when (result) {
|
||||
// is Result.Success -> {
|
||||
// // Add this courier's options to our collection
|
||||
// result.data.courierCosts?.let { costs ->
|
||||
// availableCourierOptions.addAll(costs)
|
||||
// }
|
||||
// // Update UI with what we have so far
|
||||
// _shippingOptions.value = availableCourierOptions
|
||||
// }
|
||||
// is Result.Error -> {
|
||||
// // Log the error but continue with next courier
|
||||
// Log.e("ShippingViewModel", "Error fetching cost for courier $courier: ${result.exception.message}")
|
||||
// }
|
||||
// is Result.Loading -> {
|
||||
// // Handle loading state
|
||||
// }
|
||||
// }
|
||||
// } catch (e: Exception) {
|
||||
// // Log the exception but continue with next courier
|
||||
// Log.e("ShippingViewModel", "Exception for courier $courier: ${e.message}")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Show error only if we couldn't get any shipping options
|
||||
// if (availableCourierOptions.isEmpty()) {
|
||||
// _errorMessage.value = "No shipping options available. Please try again later."
|
||||
// }
|
||||
//
|
||||
// _isLoading.value = false
|
||||
// }
|
||||
// }
|
||||
|
||||
fun loadShippingOptions(addressId: Int, productId: Int, quantity: Int) {
|
||||
// Reset previous state
|
||||
_isLoading.value = true
|
||||
_errorMessage.value = ""
|
||||
|
||||
// Prepare the request
|
||||
val costProduct = CostProduct(
|
||||
productId = productId,
|
||||
quantity = quantity
|
||||
@ -43,34 +104,47 @@ class ShippingViewModel(
|
||||
|
||||
val request = CourierCostRequest(
|
||||
addressId = addressId,
|
||||
itemCost = listOf(costProduct) // Wrap in a list
|
||||
itemCost = listOf(costProduct)
|
||||
)
|
||||
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
// Fetch courier costs
|
||||
val result = repository.getCountCourierCost(request)
|
||||
var success = false
|
||||
var attempt = 0
|
||||
val maxAttempts = 3
|
||||
|
||||
when (result) {
|
||||
is Result.Success -> {
|
||||
// Update shipping options directly with courier costs
|
||||
_shippingOptions.value = result.data.courierCosts
|
||||
}
|
||||
is Result.Error -> {
|
||||
// Handle error case
|
||||
_errorMessage.value = result.exception.message ?: "Unknown error occurred"
|
||||
}
|
||||
is Result.Loading -> {
|
||||
// Typically handled by the loading state
|
||||
while (!success && attempt < maxAttempts) {
|
||||
attempt++
|
||||
|
||||
try {
|
||||
val result = repository.getCountCourierCost(request)
|
||||
|
||||
when (result) {
|
||||
is Result.Success -> {
|
||||
_shippingOptions.value = result.data.courierCosts
|
||||
success = true
|
||||
}
|
||||
is com.alya.ecommerce_serang.data.repository.Result.Error -> {
|
||||
Log.e("ShippingViewModel", "Attempt $attempt failed: ${result.exception.message}")
|
||||
// Wait before retrying
|
||||
delay(120000)
|
||||
}
|
||||
is com.alya.ecommerce_serang.data.repository.Result.Loading -> {
|
||||
// Handle loading state
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("ShippingViewModel", "Attempt $attempt exception: ${e.message}")
|
||||
// Wait before retrying
|
||||
delay(1000)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// Catch any unexpected exceptions
|
||||
_errorMessage.value = e.localizedMessage ?: "An unexpected error occurred"
|
||||
} finally {
|
||||
// Always set loading to false
|
||||
_isLoading.value = false
|
||||
}
|
||||
|
||||
// After all attempts, check if we have any shipping options
|
||||
if (!success || _shippingOptions.value.isNullOrEmpty()) {
|
||||
_errorMessage.value = "No shipping options available. Please try again later."
|
||||
}
|
||||
|
||||
_isLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
@ -398,7 +398,7 @@ class AddAddressActivity : AppCompatActivity() {
|
||||
isRequestingLocation = false
|
||||
Toast.makeText(this, "Timeout lokasi, menggunakan lokasi default", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}, 15000) // 15 seconds timeout
|
||||
}, 60000) // 15 seconds timeout
|
||||
|
||||
// Try getting last known location first
|
||||
try {
|
||||
|
@ -5,8 +5,10 @@ import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.alya.ecommerce_serang.data.api.dto.CancelOrderReq
|
||||
import com.alya.ecommerce_serang.data.api.dto.CompletedOrderRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.OrdersItem
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.CancelOrderResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.OrderListItemsItem
|
||||
import com.alya.ecommerce_serang.data.api.response.customer.order.Orders
|
||||
import com.alya.ecommerce_serang.data.api.response.order.CompletedOrderResponse
|
||||
@ -31,6 +33,11 @@ class HistoryViewModel(private val repository: OrderRepository) : ViewModel() {
|
||||
private val _orderDetails = MutableLiveData<Orders>()
|
||||
val orderDetails: LiveData<Orders> get() = _orderDetails
|
||||
|
||||
private val _cancelOrderStatus = MutableLiveData<Result<CancelOrderResponse>>()
|
||||
val cancelOrderStatus: LiveData<Result<CancelOrderResponse>> = _cancelOrderStatus
|
||||
private val _isCancellingOrder = MutableLiveData<Boolean>()
|
||||
val isCancellingOrder: LiveData<Boolean> = _isCancellingOrder
|
||||
|
||||
// LiveData untuk OrderItems
|
||||
private val _orderItems = MutableLiveData<List<OrderListItemsItem>>()
|
||||
val orderItems: LiveData<List<OrderListItemsItem>> get() = _orderItems
|
||||
@ -131,4 +138,26 @@ class HistoryViewModel(private val repository: OrderRepository) : ViewModel() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun cancelOrder(cancelReq: CancelOrderReq) {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
_cancelOrderStatus.value = Result.Loading
|
||||
val result = repository.cancelOrder(cancelReq)
|
||||
_cancelOrderStatus.value = result
|
||||
} catch (e: Exception) {
|
||||
Log.e("HistoryViewModel", "Error cancelling order: ${e.message}")
|
||||
_cancelOrderStatus.value = Result.Error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun refreshOrders(status: String = "all") {
|
||||
Log.d(TAG, "Refreshing orders with status: $status")
|
||||
// Clear current orders before fetching new ones
|
||||
_orders.value = ViewState.Loading
|
||||
|
||||
// Re-fetch the orders with the current status
|
||||
getOrderList(status)
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package com.alya.ecommerce_serang.ui.order.history
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.ContextWrapper
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
@ -17,6 +18,7 @@ import android.widget.ImageView
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.findViewTreeLifecycleOwner
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
@ -24,6 +26,7 @@ import com.alya.ecommerce_serang.R
|
||||
import com.alya.ecommerce_serang.data.api.dto.OrdersItem
|
||||
import com.alya.ecommerce_serang.data.api.dto.ReviewUIItem
|
||||
import com.alya.ecommerce_serang.ui.order.detail.PaymentActivity
|
||||
import com.alya.ecommerce_serang.ui.order.history.cancelorder.CancelOrderBottomSheet
|
||||
import com.alya.ecommerce_serang.ui.order.review.CreateReviewActivity
|
||||
import com.alya.ecommerce_serang.ui.product.ReviewProductActivity
|
||||
import com.google.android.material.button.MaterialButton
|
||||
@ -150,7 +153,7 @@ class OrderHistoryAdapter(
|
||||
visibility = View.VISIBLE
|
||||
text = itemView.context.getString(R.string.canceled_order_btn)
|
||||
setOnClickListener {
|
||||
showCancelOrderDialog(order.orderId.toString())
|
||||
showCancelOrderBottomSheet(order.orderId)
|
||||
}
|
||||
}
|
||||
deadlineDate.apply {
|
||||
@ -171,7 +174,7 @@ class OrderHistoryAdapter(
|
||||
visibility = View.VISIBLE
|
||||
text = itemView.context.getString(R.string.canceled_order_btn)
|
||||
setOnClickListener {
|
||||
showCancelOrderDialog(order.orderId.toString())
|
||||
showCancelOrderBottomSheet(order.orderId)
|
||||
}
|
||||
}
|
||||
|
||||
@ -226,7 +229,6 @@ class OrderHistoryAdapter(
|
||||
text = itemView.context.getString(R.string.claim_complaint)
|
||||
setOnClickListener {
|
||||
showCancelOrderDialog(order.orderId.toString())
|
||||
// Handle click event
|
||||
}
|
||||
}
|
||||
btnRight.apply {
|
||||
@ -492,6 +494,48 @@ class OrderHistoryAdapter(
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
private fun showCancelOrderBottomSheet(orderId : Int) {
|
||||
val context = itemView.context
|
||||
|
||||
// We need a FragmentManager to show the bottom sheet
|
||||
// Try to get it from the context
|
||||
val fragmentActivity = when (context) {
|
||||
is FragmentActivity -> context
|
||||
is ContextWrapper -> {
|
||||
val baseContext = context.baseContext
|
||||
if (baseContext is FragmentActivity) {
|
||||
baseContext
|
||||
} else {
|
||||
// Log error and show a Toast instead if we can't get a FragmentManager
|
||||
Log.e("OrderHistoryAdapter", "Cannot show bottom sheet: Context is not a FragmentActivity")
|
||||
Toast.makeText(context, "Cannot show cancel order dialog", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// Log error and show a Toast instead if we can't get a FragmentManager
|
||||
Log.e("OrderHistoryAdapter", "Cannot show bottom sheet: Context is not a FragmentActivity")
|
||||
Toast.makeText(context, "Cannot show cancel order dialog", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Create and show the bottom sheet using the obtained FragmentManager
|
||||
val bottomSheet = CancelOrderBottomSheet(
|
||||
orderId = orderId,
|
||||
onOrderCancelled = {
|
||||
// Handle the successful cancellation
|
||||
// Refresh the data
|
||||
viewModel.refreshOrders() // Assuming there's a method to refresh orders
|
||||
|
||||
// Show a success message
|
||||
Toast.makeText(context, "Order cancelled successfully", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
)
|
||||
|
||||
bottomSheet.show(fragmentActivity.supportFragmentManager, CancelOrderBottomSheet.TAG)
|
||||
}
|
||||
|
||||
private fun addReviewProduct(order: OrdersItem) {
|
||||
// Use ViewModel to fetch order details
|
||||
viewModel.getOrderDetails(order.orderId)
|
||||
|
@ -0,0 +1,173 @@
|
||||
package com.alya.ecommerce_serang.ui.order.history.cancelorder
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AdapterView
|
||||
import android.widget.Button
|
||||
import android.widget.Spinner
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.alya.ecommerce_serang.R
|
||||
import com.alya.ecommerce_serang.data.api.dto.CancelOrderReq
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||
import com.alya.ecommerce_serang.data.repository.OrderRepository
|
||||
import com.alya.ecommerce_serang.data.repository.Result
|
||||
import com.alya.ecommerce_serang.ui.order.history.HistoryViewModel
|
||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||
import com.alya.ecommerce_serang.utils.SessionManager
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
|
||||
class CancelOrderBottomSheet(
|
||||
private val orderId: Int,
|
||||
private val onOrderCancelled: () -> Unit
|
||||
) : BottomSheetDialogFragment() {
|
||||
private lateinit var sessionManager: SessionManager
|
||||
|
||||
private val viewModel: HistoryViewModel by viewModels {
|
||||
BaseViewModelFactory {
|
||||
val apiService = ApiConfig.getApiService(sessionManager)
|
||||
val orderRepository = OrderRepository(apiService)
|
||||
HistoryViewModel(orderRepository)
|
||||
}
|
||||
}
|
||||
private var selectedReason: CancelOrderReq? = null
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.layout_cancel_order_bottom, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
sessionManager = SessionManager(requireContext())
|
||||
|
||||
val tvTitle = view.findViewById<TextView>(R.id.tv_title)
|
||||
val spinnerReason = view.findViewById<Spinner>(R.id.spinner_reason)
|
||||
val btnCancel = view.findViewById<Button>(R.id.btn_cancel)
|
||||
val btnConfirm = view.findViewById<Button>(R.id.btn_confirm)
|
||||
|
||||
// Set the title
|
||||
tvTitle.text = "Cancel Order #$orderId"
|
||||
|
||||
// Set up the spinner with cancellation reasons
|
||||
setupReasonSpinner(spinnerReason)
|
||||
|
||||
// Handle button clicks
|
||||
btnCancel.setOnClickListener {
|
||||
dismiss()
|
||||
}
|
||||
|
||||
btnConfirm.setOnClickListener {
|
||||
if (selectedReason == null) {
|
||||
Toast.makeText(context, "Please select a reason", Toast.LENGTH_SHORT).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
cancelOrder()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupReasonSpinner(spinner: Spinner) {
|
||||
val reasons = getCancellationReasons()
|
||||
val adapter = CancelReasonAdapter(requireContext(), reasons)
|
||||
spinner.adapter = adapter
|
||||
|
||||
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||
selectedReason = reasons[position]
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>?) {
|
||||
selectedReason = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCancellationReasons(): List<CancelOrderReq> {
|
||||
// These should ideally come from the server or a configuration
|
||||
return listOf(
|
||||
CancelOrderReq(1, "Changed my mind"),
|
||||
CancelOrderReq(2, "Found a better option"),
|
||||
CancelOrderReq(3, "Ordered by mistake"),
|
||||
CancelOrderReq(4, "Delivery time too long"),
|
||||
CancelOrderReq(5, "Other reason")
|
||||
)
|
||||
}
|
||||
|
||||
private fun cancelOrder() {
|
||||
// Validate reason selection
|
||||
if (selectedReason == null) {
|
||||
Toast.makeText(context, "Mohon pilih alasan pembatalan", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
// Create cancel request
|
||||
val cancelRequest = CancelOrderReq(
|
||||
orderId = orderId,
|
||||
reason = selectedReason!!.reason
|
||||
)
|
||||
Log.d(TAG, "Sending cancel request to ViewModel: orderId=${cancelRequest.orderId}, reason='${cancelRequest.reason}'")
|
||||
|
||||
|
||||
// Submit the cancellation
|
||||
viewModel.cancelOrder(cancelRequest)
|
||||
|
||||
// Observe the status
|
||||
viewModel.cancelOrderStatus.observe(viewLifecycleOwner) { result ->
|
||||
when (result) {
|
||||
is Result.Loading -> {
|
||||
// Show loading indicator
|
||||
// showLoading(true)
|
||||
}
|
||||
is Result.Success -> {
|
||||
// Hide loading indicator
|
||||
showLoading(false)
|
||||
|
||||
// Show success message
|
||||
Toast.makeText(
|
||||
context,
|
||||
"Pesanan berhasil dibatalkan",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
Log.d(TAG, "Cancel order status: SUCCESS, message: ${result.data.message}")
|
||||
|
||||
// Notify callback and close dialog
|
||||
onOrderCancelled()
|
||||
dismiss()
|
||||
}
|
||||
is Result.Error -> {
|
||||
// Hide loading indicator
|
||||
showLoading(false)
|
||||
Log.e(TAG, "Cancel order status: ERROR", result.exception)
|
||||
|
||||
|
||||
// Show error message
|
||||
val errorMsg = result.exception.message ?: "Gagal membatalkan pesanan"
|
||||
Toast.makeText(context, errorMsg, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// private fun showLoading(isLoading: Boolean) {
|
||||
// binding.progressBar.isVisible = isLoading
|
||||
// binding.btnCancel.isEnabled = !isLoading
|
||||
// binding.btnConfirm.isEnabled = !isLoading
|
||||
// }
|
||||
|
||||
private fun showLoading(isLoading: Boolean) {
|
||||
// Implement loading indicator if needed
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "CancelOrderBottomSheet"
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package com.alya.ecommerce_serang.ui.order.history.cancelorder
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.TextView
|
||||
import com.alya.ecommerce_serang.R
|
||||
import com.alya.ecommerce_serang.data.api.dto.CancelOrderReq
|
||||
|
||||
class CancelReasonAdapter(
|
||||
context: Context,
|
||||
private val reasons: List<CancelOrderReq>
|
||||
) : ArrayAdapter<CancelOrderReq>(context, 0, reasons) {
|
||||
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
return createItemView(position, convertView, parent)
|
||||
}
|
||||
|
||||
override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
return createItemView(position, convertView, parent)
|
||||
}
|
||||
|
||||
private fun createItemView(position: Int, recycledView: View?, parent: ViewGroup): View {
|
||||
val reason = getItem(position) ?: return recycledView ?: View(context)
|
||||
|
||||
val view = recycledView ?: LayoutInflater.from(context)
|
||||
.inflate(R.layout.item_cancel_order, parent, false)
|
||||
|
||||
val tvReason = view.findViewById<TextView>(R.id.tv_reason)
|
||||
tvReason.text = reason.reason
|
||||
|
||||
return view
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package com.alya.ecommerce_serang.ui.profile
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.app.ProgressDialog
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
@ -9,19 +11,24 @@ import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.alya.ecommerce_serang.BuildConfig.BASE_URL
|
||||
import com.alya.ecommerce_serang.R
|
||||
import com.alya.ecommerce_serang.data.api.dto.UserProfile
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiConfig
|
||||
import com.alya.ecommerce_serang.data.repository.UserRepository
|
||||
import com.alya.ecommerce_serang.databinding.FragmentProfileBinding
|
||||
import com.alya.ecommerce_serang.ui.auth.LoginActivity
|
||||
import com.alya.ecommerce_serang.ui.auth.RegisterStoreActivity
|
||||
import com.alya.ecommerce_serang.ui.order.address.AddressActivity
|
||||
import com.alya.ecommerce_serang.ui.order.history.HistoryActivity
|
||||
import com.alya.ecommerce_serang.ui.profile.mystore.MyStoreActivity
|
||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||
import com.alya.ecommerce_serang.utils.SessionManager
|
||||
import com.alya.ecommerce_serang.utils.viewmodel.ProfileViewModel
|
||||
import com.bumptech.glide.Glide
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ProfileFragment : Fragment() {
|
||||
|
||||
@ -85,6 +92,16 @@ class ProfileFragment : Fragment() {
|
||||
val intent = Intent(requireContext(), HistoryActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
binding.cardLogout.setOnClickListener({
|
||||
logout()
|
||||
|
||||
})
|
||||
|
||||
binding.cardAddress.setOnClickListener({
|
||||
val intent = Intent(requireContext(), AddressActivity::class.java)
|
||||
startActivity(intent)
|
||||
})
|
||||
}
|
||||
|
||||
private fun observeUserProfile() {
|
||||
@ -115,4 +132,41 @@ class ProfileFragment : Fragment() {
|
||||
.placeholder(R.drawable.placeholder_image)
|
||||
.into(profileImage)
|
||||
}
|
||||
|
||||
private fun logout(){
|
||||
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setTitle("Konfirmasi")
|
||||
.setMessage("Apakah anda yakin ingin keluar?")
|
||||
.setPositiveButton("Ya") { _, _ ->
|
||||
actionLogout()
|
||||
}
|
||||
.setNegativeButton("Tidak", null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun actionLogout(){
|
||||
val loadingDialog = ProgressDialog(requireContext()).apply {
|
||||
setMessage("Mohon ditunggu")
|
||||
setCancelable(false)
|
||||
show()
|
||||
}
|
||||
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
delay(500)
|
||||
loadingDialog.dismiss()
|
||||
sessionManager.clearAll()
|
||||
val intent = Intent(requireContext(), LoginActivity::class.java)
|
||||
startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
"Gagal keluar: ${e.message}",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -57,8 +57,6 @@ class SessionManager(context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//clear data when log out
|
||||
fun clearAll() {
|
||||
sharedPreferences.edit() {
|
||||
|
@ -27,6 +27,9 @@ class ProfileViewModel(private val userRepository: UserRepository) : ViewModel()
|
||||
private val _checkStore = MutableLiveData<Boolean>()
|
||||
val checkStore: LiveData<Boolean> = _checkStore
|
||||
|
||||
private val _logout = MutableLiveData<Boolean>()
|
||||
val logout : LiveData<Boolean> = _checkStore
|
||||
|
||||
fun loadUserProfile(){
|
||||
viewModelScope.launch {
|
||||
when (val result = userRepository.fetchUserProfile()){
|
||||
@ -57,8 +60,6 @@ class ProfileViewModel(private val userRepository: UserRepository) : ViewModel()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun editProfileDirect(
|
||||
context: Context,
|
||||
username: String,
|
||||
@ -97,6 +98,17 @@ class ProfileViewModel(private val userRepository: UserRepository) : ViewModel()
|
||||
}
|
||||
}
|
||||
|
||||
fun logout(){
|
||||
viewModelScope.launch {
|
||||
try{
|
||||
|
||||
} catch (e: Exception){
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
private const val TAG = "ProfileViewModel"
|
||||
}
|
||||
|
5
app/src/main/res/drawable/baseline_info_24.xml
Normal file
5
app/src/main/res/drawable/baseline_info_24.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#211E1E" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/>
|
||||
|
||||
</vector>
|
5
app/src/main/res/drawable/baseline_logout_24.xml
Normal file
5
app/src/main/res/drawable/baseline_logout_24.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="#211E1E" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M17,7l-1.41,1.41L18.17,11H8v2h10.17l-2.58,2.58L17,17l5,-5zM4,5h8V3H4c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h8v-2H4V5z"/>
|
||||
|
||||
</vector>
|
8
app/src/main/res/drawable/bg_bottom_sheet.xml
Normal file
8
app/src/main/res/drawable/bg_bottom_sheet.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="@color/white" />
|
||||
<corners
|
||||
android:topLeftRadius="16dp"
|
||||
android:topRightRadius="16dp" />
|
||||
</shape>
|
8
app/src/main/res/drawable/bg_spinner_reason.xml
Normal file
8
app/src/main/res/drawable/bg_spinner_reason.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/white" />
|
||||
<corners android:radius="8dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/light_gray" />
|
||||
</shape>
|
@ -231,13 +231,14 @@
|
||||
android:text="10. Bank *"
|
||||
android:textColor="@android:color/black" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner_bank"
|
||||
<EditText
|
||||
android:id="@+id/et_bank_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@android:drawable/btn_dropdown"
|
||||
android:hint="Pilih jawaban Anda di sini"
|
||||
android:background="@android:drawable/editbox_background"
|
||||
android:hint="Isi jawaban Anda di sini"
|
||||
android:inputType="text"
|
||||
android:padding="12dp" />
|
||||
|
||||
<TextView
|
||||
|
@ -118,6 +118,7 @@
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:paddingVertical="8dp"
|
||||
android:textSize="16sp"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:text="Pesanan Saya"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@ -129,7 +130,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:textSize="12sp"
|
||||
android:textSize="14sp"
|
||||
android:padding="0dp"
|
||||
android:fontFamily="@font/dmsans_light"
|
||||
android:text="Lihat Riwayat Pesanan"
|
||||
@ -164,6 +165,8 @@
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp"
|
||||
android:fontFamily="@font/dmsans_regular"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/waiting_payment" />
|
||||
</LinearLayout>
|
||||
@ -185,6 +188,8 @@
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/dmsans_regular"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/packages" />
|
||||
</LinearLayout>
|
||||
@ -206,6 +211,8 @@
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/dmsans_regular"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/delivery" />
|
||||
</LinearLayout>
|
||||
@ -223,103 +230,176 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:padding="16dp"
|
||||
android:textSize="16sp"
|
||||
android:text="Pengaturan Akun"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
app:layout_constraintTop_toBottomOf="@id/cardPesanan" />
|
||||
|
||||
<!-- Address -->
|
||||
<ImageView
|
||||
android:id="@+id/ivAddress"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:src="@drawable/ic_address"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tvAddress"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tvAddress" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvAddress"
|
||||
android:layout_width="0dp"
|
||||
<LinearLayout
|
||||
android:id="@+id/container_settings"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
android:text="Alamat"
|
||||
app:layout_constraintEnd_toStartOf="@id/ivAddressArrow"
|
||||
app:layout_constraintStart_toEndOf="@id/ivAddress"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvPengaturanAkun" />
|
||||
app:layout_constraintTop_toBottomOf="@id/tvPengaturanAkun"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<!-- Address Card -->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/card_address"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:cardElevation="2dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivAddress"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/ic_address"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvAddress"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:text="Alamat"
|
||||
android:textSize="14sp"
|
||||
android:fontFamily="@font/dmsans_regular"
|
||||
app:layout_constraintStart_toEndOf="@id/ivAddress"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/ivAddressArrow" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivAddressArrow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_arrow_right"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<!-- About Card -->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/card_about"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:cardElevation="2dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivAbout"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/baseline_info_24"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvAbout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:text="Tentang"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintStart_toEndOf="@id/ivAbout"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/ivAboutArrow" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivAboutArrow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_arrow_right"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<!-- Logout Card -->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/card_logout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:cardElevation="2dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivLogout"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/baseline_logout_24"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvLogout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:text="Keluar"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintStart_toEndOf="@id/ivLogout"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/ivLogoutArrow" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivLogoutArrow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_arrow_right"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivAddressArrow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:src="@drawable/ic_arrow_right"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tvAddress"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tvAddress" />
|
||||
|
||||
<!-- About -->
|
||||
<ImageView
|
||||
android:id="@+id/ivAbout"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tvAbout"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tvAbout" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvAbout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:padding="16dp"
|
||||
android:text="Tentang"
|
||||
app:layout_constraintEnd_toStartOf="@id/ivAboutArrow"
|
||||
app:layout_constraintStart_toEndOf="@id/ivAbout"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvAddress" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivAboutArrow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:src="@drawable/ic_arrow_right"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tvAbout"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tvAbout" />
|
||||
|
||||
<!-- Logout -->
|
||||
<ImageView
|
||||
android:id="@+id/ivLogout"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tvLogout"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tvLogout" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvLogout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:padding="16dp"
|
||||
android:text="Keluar"
|
||||
app:layout_constraintEnd_toStartOf="@id/ivLogoutArrow"
|
||||
app:layout_constraintStart_toEndOf="@id/ivLogout"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvAbout" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivLogoutArrow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:src="@drawable/ic_arrow_right"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tvLogout"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tvLogout" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
8
app/src/main/res/layout/item_cancel_order.xml
Normal file
8
app/src/main/res/layout/item_cancel_order.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/tv_reason"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="14sp" />
|
@ -1,16 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="start"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginVertical="8dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:orientation="horizontal">
|
||||
<RadioButton
|
||||
@ -27,7 +29,7 @@
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:id="@+id/courier_name_cost"
|
||||
android:fontFamily="@font/dmsans_bold"
|
||||
android:fontFamily="@font/dmsans_semibold"
|
||||
android:textSize="20sp"
|
||||
android:padding="4dp"
|
||||
android:layout_width="wrap_content"
|
||||
@ -40,8 +42,8 @@
|
||||
android:textSize="16sp"
|
||||
android:paddingHorizontal="8dp"
|
||||
android:text="Estimasi 3-4 hari"/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/cost_price"
|
||||
@ -53,12 +55,16 @@
|
||||
android:layout_height="match_parent"
|
||||
android:text="Rp15.0000"/>
|
||||
|
||||
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
<View
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:background="#E0E0E0"
|
||||
app:layout_constraintTop_toBottomOf="@id/linear_toolbar" />
|
||||
app:layout_constraintTop_toBottomOf="@id/content" />
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
73
app/src/main/res/layout/layout_cancel_order_bottom.xml
Normal file
73
app/src/main/res/layout/layout_cancel_order_bottom.xml
Normal file
@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_bottom_sheet"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/dmsans_semibold"
|
||||
android:text="Batalkan Pesanan"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_reason_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="Alasan batalkan pesanan:"
|
||||
android:fontFamily="@font/dmsans_regular"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/tv_title" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner_reason"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/bg_spinner_reason"
|
||||
android:padding="12dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/tv_reason_label" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_cancel"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:backgroundTint="@color/black_200"
|
||||
android:text="Kembali"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:textColor="@color/white"
|
||||
app:layout_constraintEnd_toStartOf="@+id/btn_confirm"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/spinner_reason" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_confirm"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:backgroundTint="@color/blue_500"
|
||||
android:text="Batal"
|
||||
android:fontFamily="@font/dmsans_medium"
|
||||
android:textColor="@color/white"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/btn_cancel"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/btn_cancel"
|
||||
app:layout_constraintTop_toTopOf="@+id/btn_cancel" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
Reference in New Issue
Block a user