mirror of
https://github.com/shaulascr/ecommerce_serang.git
synced 2025-08-13 18:52:20 +00:00
add complaint (in dialog)
This commit is contained in:
@ -0,0 +1,16 @@
|
|||||||
|
package com.alya.ecommerce_serang.data.api.dto
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
import okhttp3.MultipartBody
|
||||||
|
|
||||||
|
data class ComplaintRequest (
|
||||||
|
@SerializedName("order_id")
|
||||||
|
val orderId: Int,
|
||||||
|
|
||||||
|
@SerializedName("description")
|
||||||
|
val description: String,
|
||||||
|
|
||||||
|
@SerializedName("complaintimg")
|
||||||
|
val complaintImg: MultipartBody.Part
|
||||||
|
|
||||||
|
)
|
@ -0,0 +1,36 @@
|
|||||||
|
package com.alya.ecommerce_serang.data.api.response.order
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
data class ComplaintResponse(
|
||||||
|
|
||||||
|
@field:SerializedName("voucher")
|
||||||
|
val voucher: Voucher,
|
||||||
|
|
||||||
|
@field:SerializedName("message")
|
||||||
|
val message: String
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Voucher(
|
||||||
|
|
||||||
|
@field:SerializedName("solution")
|
||||||
|
val solution: Any,
|
||||||
|
|
||||||
|
@field:SerializedName("evidence")
|
||||||
|
val evidence: String,
|
||||||
|
|
||||||
|
@field:SerializedName("description")
|
||||||
|
val description: String,
|
||||||
|
|
||||||
|
@field:SerializedName("created_at")
|
||||||
|
val createdAt: String,
|
||||||
|
|
||||||
|
@field:SerializedName("id")
|
||||||
|
val id: Int,
|
||||||
|
|
||||||
|
@field:SerializedName("order_id")
|
||||||
|
val orderId: Int,
|
||||||
|
|
||||||
|
@field:SerializedName("status")
|
||||||
|
val status: String
|
||||||
|
)
|
@ -19,6 +19,7 @@ import com.alya.ecommerce_serang.data.api.response.cart.AddCartResponse
|
|||||||
import com.alya.ecommerce_serang.data.api.response.cart.ListCartResponse
|
import com.alya.ecommerce_serang.data.api.response.cart.ListCartResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.cart.UpdateCartResponse
|
import com.alya.ecommerce_serang.data.api.response.cart.UpdateCartResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.order.AddEvidenceResponse
|
import com.alya.ecommerce_serang.data.api.response.order.AddEvidenceResponse
|
||||||
|
import com.alya.ecommerce_serang.data.api.response.order.ComplaintResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.order.CompletedOrderResponse
|
import com.alya.ecommerce_serang.data.api.response.order.CompletedOrderResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.order.CourierCostResponse
|
import com.alya.ecommerce_serang.data.api.response.order.CourierCostResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.order.CreateOrderResponse
|
import com.alya.ecommerce_serang.data.api.response.order.CreateOrderResponse
|
||||||
@ -187,4 +188,12 @@ interface ApiService {
|
|||||||
suspend fun confirmOrder(
|
suspend fun confirmOrder(
|
||||||
@Body confirmOrder : CompletedOrderRequest
|
@Body confirmOrder : CompletedOrderRequest
|
||||||
): Response<CompletedOrderResponse>
|
): Response<CompletedOrderResponse>
|
||||||
|
|
||||||
|
@Multipart
|
||||||
|
@POST("addcomplaint")
|
||||||
|
suspend fun addComplaint(
|
||||||
|
@Part("order_id") orderId: RequestBody,
|
||||||
|
@Part("description") description: RequestBody,
|
||||||
|
@Part complaintimg: MultipartBody.Part
|
||||||
|
): Response<ComplaintResponse>
|
||||||
}
|
}
|
@ -10,6 +10,7 @@ import com.alya.ecommerce_serang.data.api.dto.OrderRequestBuy
|
|||||||
import com.alya.ecommerce_serang.data.api.dto.UserProfile
|
import com.alya.ecommerce_serang.data.api.dto.UserProfile
|
||||||
import com.alya.ecommerce_serang.data.api.response.cart.DataItem
|
import com.alya.ecommerce_serang.data.api.response.cart.DataItem
|
||||||
import com.alya.ecommerce_serang.data.api.response.order.AddEvidenceResponse
|
import com.alya.ecommerce_serang.data.api.response.order.AddEvidenceResponse
|
||||||
|
import com.alya.ecommerce_serang.data.api.response.order.ComplaintResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.order.CompletedOrderResponse
|
import com.alya.ecommerce_serang.data.api.response.order.CompletedOrderResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.order.CourierCostResponse
|
import com.alya.ecommerce_serang.data.api.response.order.CourierCostResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.order.CreateOrderResponse
|
import com.alya.ecommerce_serang.data.api.response.order.CreateOrderResponse
|
||||||
@ -23,7 +24,16 @@ import com.alya.ecommerce_serang.data.api.response.product.StoreResponse
|
|||||||
import com.alya.ecommerce_serang.data.api.response.profile.AddressResponse
|
import com.alya.ecommerce_serang.data.api.response.profile.AddressResponse
|
||||||
import com.alya.ecommerce_serang.data.api.response.profile.CreateAddressResponse
|
import com.alya.ecommerce_serang.data.api.response.profile.CreateAddressResponse
|
||||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiService
|
import com.alya.ecommerce_serang.data.api.retrofit.ApiService
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||||
|
import okhttp3.MultipartBody
|
||||||
|
import okhttp3.RequestBody.Companion.asRequestBody
|
||||||
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
class OrderRepository(private val apiService: ApiService) {
|
class OrderRepository(private val apiService: ApiService) {
|
||||||
|
|
||||||
@ -351,4 +361,75 @@ suspend fun uploadPaymentProof(request: AddEvidenceMultipartRequest): Result<Add
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun submitComplaint(
|
||||||
|
orderId: String,
|
||||||
|
reason: String,
|
||||||
|
imageFile: File?
|
||||||
|
): Flow<Result<ComplaintResponse>> = flow {
|
||||||
|
emit(Result.Loading)
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Debug logging
|
||||||
|
Log.d("OrderRepository", "Submitting complaint for order: $orderId")
|
||||||
|
Log.d("OrderRepository", "Reason: $reason")
|
||||||
|
Log.d("OrderRepository", "Image file: ${imageFile?.absolutePath ?: "null"}")
|
||||||
|
|
||||||
|
// Create form data for the multipart request
|
||||||
|
// Explicitly convert orderId to string to ensure correct formatting
|
||||||
|
val orderIdRequestBody = orderId.toString().toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
|
val reasonRequestBody = reason.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
|
|
||||||
|
// Create the image part for the API
|
||||||
|
val imagePart = if (imageFile != null && imageFile.exists()) {
|
||||||
|
// Use the actual image file
|
||||||
|
// Use asRequestBody() for files which is more efficient
|
||||||
|
val imageRequestBody = imageFile.asRequestBody("image/*".toMediaTypeOrNull())
|
||||||
|
MultipartBody.Part.createFormData(
|
||||||
|
"complaintimg",
|
||||||
|
imageFile.name,
|
||||||
|
imageRequestBody
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Create a simple empty part if no image
|
||||||
|
val dummyRequestBody = "".toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
|
MultipartBody.Part.createFormData(
|
||||||
|
"complaintimg",
|
||||||
|
"",
|
||||||
|
dummyRequestBody
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log request details before making the API call
|
||||||
|
Log.d("OrderRepository", "Making API call to add complaint")
|
||||||
|
Log.d("OrderRepository", "orderId: $orderId (as string)")
|
||||||
|
|
||||||
|
val response = apiService.addComplaint(
|
||||||
|
orderIdRequestBody,
|
||||||
|
reasonRequestBody,
|
||||||
|
imagePart
|
||||||
|
)
|
||||||
|
|
||||||
|
Log.d("OrderRepository", "Response code: ${response.code()}")
|
||||||
|
Log.d("OrderRepository", "Response message: ${response.message()}")
|
||||||
|
|
||||||
|
if (response.isSuccessful && response.body() != null) {
|
||||||
|
val complaintResponse = response.body() as ComplaintResponse
|
||||||
|
emit(Result.Success(complaintResponse))
|
||||||
|
} else {
|
||||||
|
// Get the error message from the response if possible
|
||||||
|
val errorBody = response.errorBody()?.string()
|
||||||
|
val errorMessage = if (!errorBody.isNullOrEmpty()) {
|
||||||
|
"Server error: $errorBody"
|
||||||
|
} else {
|
||||||
|
"Failed to submit complaint: ${response.code()} ${response.message()}"
|
||||||
|
}
|
||||||
|
Log.e("OrderRepository", errorMessage)
|
||||||
|
emit(Result.Error(Exception(errorMessage)))
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("OrderRepository", "Error submitting complaint: ${e.message}")
|
||||||
|
emit(Result.Error(e))
|
||||||
|
}
|
||||||
|
}.flowOn(Dispatchers.IO)
|
||||||
|
|
||||||
}
|
}
|
@ -12,6 +12,7 @@ import com.alya.ecommerce_serang.data.repository.OrderRepository
|
|||||||
import com.alya.ecommerce_serang.data.repository.Result
|
import com.alya.ecommerce_serang.data.repository.Result
|
||||||
import com.alya.ecommerce_serang.ui.order.address.ViewState
|
import com.alya.ecommerce_serang.ui.order.address.ViewState
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
class HistoryViewModel(private val repository: OrderRepository) : ViewModel() {
|
class HistoryViewModel(private val repository: OrderRepository) : ViewModel() {
|
||||||
|
|
||||||
@ -25,6 +26,15 @@ class HistoryViewModel(private val repository: OrderRepository) : ViewModel() {
|
|||||||
private val _orderCompletionStatus = MutableLiveData<Result<CompletedOrderResponse>>()
|
private val _orderCompletionStatus = MutableLiveData<Result<CompletedOrderResponse>>()
|
||||||
val orderCompletionStatus: LiveData<Result<CompletedOrderResponse>> = _orderCompletionStatus
|
val orderCompletionStatus: LiveData<Result<CompletedOrderResponse>> = _orderCompletionStatus
|
||||||
|
|
||||||
|
private val _isLoading = MutableLiveData<Boolean>()
|
||||||
|
val isLoading: LiveData<Boolean> = _isLoading
|
||||||
|
|
||||||
|
private val _message = MutableLiveData<String>()
|
||||||
|
val message: LiveData<String> = _message
|
||||||
|
|
||||||
|
private val _isSuccess = MutableLiveData<Boolean>()
|
||||||
|
val isSuccess: LiveData<Boolean> = _isSuccess
|
||||||
|
|
||||||
fun getOrderList(status: String) {
|
fun getOrderList(status: String) {
|
||||||
_orders.value = ViewState.Loading
|
_orders.value = ViewState.Loading
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
@ -51,12 +61,42 @@ class HistoryViewModel(private val repository: OrderRepository) : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fun confirmOrderCompleted(orderId: Int, status: String) {
|
fun confirmOrderCompleted(orderId: Int, status: String) {
|
||||||
|
Log.d(TAG, "Confirming order completed: orderId=$orderId, status=$status")
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_orderCompletionStatus.value = Result.Loading
|
_orderCompletionStatus.value = Result.Loading
|
||||||
val request = CompletedOrderRequest(orderId, status)
|
val request = CompletedOrderRequest(orderId, status)
|
||||||
|
|
||||||
|
Log.d(TAG, "Sending order completion request: $request")
|
||||||
val result = repository.confirmOrderCompleted(request)
|
val result = repository.confirmOrderCompleted(request)
|
||||||
|
Log.d(TAG, "Order completion result: $result")
|
||||||
_orderCompletionStatus.value = result
|
_orderCompletionStatus.value = result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun cancelOrderWithImage(orderId: String, reason: String, imageFile: File?) {
|
||||||
|
Log.d(TAG, "Cancelling order with image: orderId=$orderId, reason=$reason, hasImage=${imageFile != null}")
|
||||||
|
viewModelScope.launch {
|
||||||
|
repository.submitComplaint(orderId, reason, imageFile).collect { result ->
|
||||||
|
when (result) {
|
||||||
|
is Result.Loading -> {
|
||||||
|
Log.d(TAG, "Submitting complaint: Loading")
|
||||||
|
_isLoading.value = true
|
||||||
|
}
|
||||||
|
is Result.Success -> {
|
||||||
|
Log.d(TAG, "Complaint submitted successfully: ${result.data.message}")
|
||||||
|
_message.value = result.data.message
|
||||||
|
_isSuccess.value = true
|
||||||
|
_isLoading.value = false
|
||||||
|
}
|
||||||
|
is Result.Error -> {
|
||||||
|
val errorMessage = result.exception.message ?: "Error submitting complaint"
|
||||||
|
Log.e(TAG, "Error submitting complaint: $errorMessage", result.exception)
|
||||||
|
_message.value = errorMessage
|
||||||
|
_isSuccess.value = false
|
||||||
|
_isLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,17 +1,31 @@
|
|||||||
package com.alya.ecommerce_serang.ui.order.history
|
package com.alya.ecommerce_serang.ui.order.history
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.Dialog
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.net.Uri
|
||||||
|
import android.provider.MediaStore
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.view.Window
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
|
import android.widget.AutoCompleteTextView
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.ProgressBar
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.lifecycle.findViewTreeLifecycleOwner
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.alya.ecommerce_serang.R
|
import com.alya.ecommerce_serang.R
|
||||||
import com.alya.ecommerce_serang.data.api.response.order.OrdersItem
|
import com.alya.ecommerce_serang.data.api.response.order.OrdersItem
|
||||||
import com.alya.ecommerce_serang.ui.order.detail.PaymentActivity
|
import com.alya.ecommerce_serang.ui.order.detail.PaymentActivity
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
|
import java.io.File
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
@ -128,6 +142,13 @@ class OrderHistoryAdapter(
|
|||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
text = itemView.context.getString(R.string.dl_pending)
|
text = itemView.context.getString(R.string.dl_pending)
|
||||||
}
|
}
|
||||||
|
btnLeft.apply {
|
||||||
|
visibility = View.VISIBLE
|
||||||
|
text = itemView.context.getString(R.string.canceled_order_btn)
|
||||||
|
setOnClickListener {
|
||||||
|
showCancelOrderDialog(order.orderId.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
deadlineDate.apply {
|
deadlineDate.apply {
|
||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
text = formatDate(order.createdAt)
|
text = formatDate(order.createdAt)
|
||||||
@ -146,6 +167,7 @@ class OrderHistoryAdapter(
|
|||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
text = itemView.context.getString(R.string.canceled_order_btn)
|
text = itemView.context.getString(R.string.canceled_order_btn)
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
|
showCancelOrderDialog(order.orderId.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,7 +199,13 @@ class OrderHistoryAdapter(
|
|||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
text = itemView.context.getString(R.string.dl_processed)
|
text = itemView.context.getString(R.string.dl_processed)
|
||||||
}
|
}
|
||||||
|
btnLeft.apply {
|
||||||
|
visibility = View.VISIBLE
|
||||||
|
text = itemView.context.getString(R.string.canceled_order_btn)
|
||||||
|
setOnClickListener {
|
||||||
|
showCancelOrderDialog(order.orderId.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"shipped" -> {
|
"shipped" -> {
|
||||||
// Untuk status shipped, tampilkan "Lacak Pengiriman" dan "Terima Barang"
|
// Untuk status shipped, tampilkan "Lacak Pengiriman" dan "Terima Barang"
|
||||||
@ -193,6 +221,7 @@ class OrderHistoryAdapter(
|
|||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
text = itemView.context.getString(R.string.claim_complaint)
|
text = itemView.context.getString(R.string.claim_complaint)
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
|
showCancelOrderDialog(order.orderId.toString())
|
||||||
// Handle click event
|
// Handle click event
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -318,5 +347,158 @@ class OrderHistoryAdapter(
|
|||||||
dateString
|
dateString
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showCancelOrderDialog(orderId: String) {
|
||||||
|
val context = itemView.context
|
||||||
|
val dialog = Dialog(context)
|
||||||
|
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||||
|
dialog.setContentView(R.layout.dialog_cancel_order)
|
||||||
|
dialog.setCancelable(true)
|
||||||
|
|
||||||
|
// Set the dialog width to match parent
|
||||||
|
val window = dialog.window
|
||||||
|
window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||||
|
|
||||||
|
// Get references to the views in the dialog
|
||||||
|
val spinnerCancelReason = dialog.findViewById<AutoCompleteTextView>(R.id.spinnerCancelReason)
|
||||||
|
val tilCancelReason = dialog.findViewById<TextInputLayout>(R.id.tilCancelReason)
|
||||||
|
val btnCancelDialog = dialog.findViewById<MaterialButton>(R.id.btnCancelDialog)
|
||||||
|
val btnConfirmCancel = dialog.findViewById<MaterialButton>(R.id.btnConfirmCancel)
|
||||||
|
val ivComplaintImage = dialog.findViewById<ImageView>(R.id.ivComplaintImage)
|
||||||
|
val tvSelectImage = dialog.findViewById<TextView>(R.id.tvSelectImage)
|
||||||
|
|
||||||
|
// Set up the reasons dropdown
|
||||||
|
val reasons = context.resources.getStringArray(R.array.cancellation_reasons)
|
||||||
|
val adapter = ArrayAdapter(context, android.R.layout.simple_dropdown_item_1line, reasons)
|
||||||
|
spinnerCancelReason.setAdapter(adapter)
|
||||||
|
|
||||||
|
// For storing the selected image URI
|
||||||
|
var selectedImageUri: Uri? = null
|
||||||
|
|
||||||
|
// Set click listener for image selection
|
||||||
|
ivComplaintImage.setOnClickListener {
|
||||||
|
// Create an intent to open the image picker
|
||||||
|
val galleryIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
|
||||||
|
(context as? Activity)?.startActivityForResult(galleryIntent, REQUEST_IMAGE_PICK)
|
||||||
|
|
||||||
|
// Set up result handler in the activity
|
||||||
|
val activity = context as? Activity
|
||||||
|
activity?.let {
|
||||||
|
// Remove any existing callbacks to avoid memory leaks
|
||||||
|
if (imagePickCallback != null) {
|
||||||
|
imagePickCallback = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new callback for this specific dialog
|
||||||
|
imagePickCallback = { uri ->
|
||||||
|
selectedImageUri = uri
|
||||||
|
|
||||||
|
// Load and display the selected image
|
||||||
|
ivComplaintImage.setImageURI(uri)
|
||||||
|
tvSelectImage.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set click listeners for buttons
|
||||||
|
btnCancelDialog.setOnClickListener {
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
btnConfirmCancel.setOnClickListener {
|
||||||
|
val reason = spinnerCancelReason.text.toString().trim()
|
||||||
|
|
||||||
|
if (reason.isEmpty()) {
|
||||||
|
tilCancelReason.error = context.getString(R.string.please_select_cancellation_reason)
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear error if any
|
||||||
|
tilCancelReason.error = null
|
||||||
|
|
||||||
|
// Convert selected image to file if available
|
||||||
|
val imageFile = selectedImageUri?.let { uri ->
|
||||||
|
try {
|
||||||
|
// Get the file path from URI
|
||||||
|
val filePathColumn = arrayOf(MediaStore.Images.Media.DATA)
|
||||||
|
val cursor = context.contentResolver.query(uri, filePathColumn, null, null, null)
|
||||||
|
cursor?.use {
|
||||||
|
if (it.moveToFirst()) {
|
||||||
|
val columnIndex = it.getColumnIndex(filePathColumn[0])
|
||||||
|
val filePath = it.getString(columnIndex)
|
||||||
|
return@let File(filePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
null
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("OrderHistoryAdapter", "Error getting file from URI: ${e.message}")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show loading indicator
|
||||||
|
val loadingView = View(context).apply {
|
||||||
|
layoutParams = ViewGroup.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
|
)
|
||||||
|
setBackgroundColor(Color.parseColor("#80000000"))
|
||||||
|
|
||||||
|
val progressBar = ProgressBar(context).apply {
|
||||||
|
layoutParams = ViewGroup.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// addView(progressBar)
|
||||||
|
// (progressBar.layoutParams as? ViewGroup.MarginLayoutParams)?.apply {
|
||||||
|
// gravity = Gravity.CENTER
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.addContentView(loadingView, loadingView.layoutParams)
|
||||||
|
|
||||||
|
// Call the ViewModel to cancel the order with image
|
||||||
|
viewModel.cancelOrderWithImage(orderId, reason, imageFile)
|
||||||
|
|
||||||
|
// Observe for success/failure
|
||||||
|
viewModel.isSuccess.observe(itemView.findViewTreeLifecycleOwner()!!) { isSuccess ->
|
||||||
|
// Remove loading indicator
|
||||||
|
(loadingView.parent as? ViewGroup)?.removeView(loadingView)
|
||||||
|
|
||||||
|
if (isSuccess) {
|
||||||
|
Toast.makeText(context, context.getString(R.string.order_canceled_successfully), Toast.LENGTH_SHORT).show()
|
||||||
|
dialog.dismiss()
|
||||||
|
|
||||||
|
// Find the order in the list and remove it or update its status
|
||||||
|
val position = orders.indexOfFirst { it.orderId.toString() == orderId }
|
||||||
|
if (position != -1) {
|
||||||
|
orders.removeAt(position)
|
||||||
|
notifyItemRemoved(position)
|
||||||
|
notifyItemRangeChanged(position, orders.size)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Toast.makeText(context, viewModel.message.value ?: context.getString(R.string.failed_to_cancel_order), Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dialog.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val REQUEST_IMAGE_PICK = 100
|
||||||
|
private var imagePickCallback: ((Uri) -> Unit)? = null
|
||||||
|
|
||||||
|
// This method should be called from the activity's onActivityResult
|
||||||
|
fun handleImageResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
if (requestCode == REQUEST_IMAGE_PICK && resultCode == Activity.RESULT_OK && data != null) {
|
||||||
|
val selectedImageUri = data.data
|
||||||
|
selectedImageUri?.let { uri ->
|
||||||
|
imagePickCallback?.invoke(uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -52,23 +52,6 @@ class HomeViewModel (
|
|||||||
loadProducts()
|
loadProducts()
|
||||||
loadCategories()
|
loadCategories()
|
||||||
}
|
}
|
||||||
|
|
||||||
// private fun fetchUserData() {
|
|
||||||
// viewModelScope.launch {
|
|
||||||
// try {
|
|
||||||
// val response = apiService.getProtectedData() // Example API request
|
|
||||||
// if (response.isSuccessful) {
|
|
||||||
// val data = response.body()
|
|
||||||
// Log.d("HomeFragment", "User Data: $data")
|
|
||||||
// // Update UI with data
|
|
||||||
// } else {
|
|
||||||
// Log.e("HomeFragment", "Error: ${response.message()}")
|
|
||||||
// }
|
|
||||||
// } catch (e: Exception) {
|
|
||||||
// Log.e("HomeFragment", "Exception: ${e.message}")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class HomeUiState {
|
sealed class HomeUiState {
|
||||||
|
11
app/src/main/res/drawable/bg_dashboard_border.xml
Normal file
11
app/src/main/res/drawable/bg_dashboard_border.xml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<stroke
|
||||||
|
android:width="2dp"
|
||||||
|
android:color="#BCBCBC"
|
||||||
|
android:dashWidth="10dp"
|
||||||
|
android:dashGap="6dp" />
|
||||||
|
<corners android:radius="8dp" />
|
||||||
|
<solid android:color="#F5F5F5" />
|
||||||
|
</shape>
|
103
app/src/main/res/layout/dialog_cancel_order.xml
Normal file
103
app/src/main/res/layout/dialog_cancel_order.xml
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.cardview.widget.CardView 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"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
app:cardElevation="8dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:text="@string/cancel_order_confirmation"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:fontFamily="@font/dmsans_semibold"
|
||||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6" />
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/tilCancelReason"
|
||||||
|
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:hint="@string/reason_for_cancellation">
|
||||||
|
|
||||||
|
<AutoCompleteTextView
|
||||||
|
android:id="@+id/spinnerCancelReason"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="none"
|
||||||
|
android:focusable="false" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<!-- Image Upload Section -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/upload_evidence"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:fontFamily="@font/dmsans_medium"
|
||||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" />
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ivComplaintImage"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="150dp"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:background="@drawable/bg_dashboard_border"
|
||||||
|
android:contentDescription="@string/complaint_image" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvSelectImage"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:text="@string/tap_to_select_image"
|
||||||
|
android:drawableTop="@drawable/baseline_upload_file_24"
|
||||||
|
android:drawablePadding="8dp"
|
||||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2" />
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnCancelDialog"
|
||||||
|
style="@style/RoundedBorderStyle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:fontFamily="@font/dmsans_semibold"
|
||||||
|
android:textColor="@color/blue_500"
|
||||||
|
android:text="@string/cancel" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnConfirmCancel"
|
||||||
|
style="@style/RoundedBorderStyleFilled"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:fontFamily="@font/dmsans_semibold"
|
||||||
|
android:text="@string/confirm" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
@ -93,5 +93,29 @@
|
|||||||
<string name="claim_order">Pesanan Diterima </string>
|
<string name="claim_order">Pesanan Diterima </string>
|
||||||
<string name="add_review">Beri Ulasan </string>
|
<string name="add_review">Beri Ulasan </string>
|
||||||
|
|
||||||
|
<string name="warning_icon">Warning Icon</string>
|
||||||
|
<string name="cancel_order_confirmation">Apakah anda yakin ingin membatalkan pesanan?</string>
|
||||||
|
<string name="reason_for_cancellation">Alasan Batalkan Pesanan</string>
|
||||||
|
<string name="cancel">Kembali</string>
|
||||||
|
<string name="confirm">Batalkan Pesanan</string>
|
||||||
|
<string name="order_canceled_successfully">Order canceled successfully</string>
|
||||||
|
<string name="failed_to_cancel_order">Failed to cancel order</string>
|
||||||
|
<string name="please_select_cancellation_reason">Please select a reason for cancellation</string>
|
||||||
|
<string name="upload_evidence">Unggah Bukti Komplain</string>
|
||||||
|
<string name="complaint_image">Complaint evidence image</string>
|
||||||
|
<string name="tap_to_select_image">Tekan untuk unggah foto</string>
|
||||||
|
<string name="please_select_image">Please select an image as evidence</string>
|
||||||
|
<string name="image_too_large">Image is too large. Please select a smaller image.</string>
|
||||||
|
|
||||||
|
<!-- Cancellation Reasons -->
|
||||||
|
<string-array name="cancellation_reasons">
|
||||||
|
<item>Found a better price elsewhere</item>
|
||||||
|
<item>Changed my mind about the product</item>
|
||||||
|
<item>Ordered the wrong item</item>
|
||||||
|
<item>Shipping time is too long</item>
|
||||||
|
<item>Financial reasons</item>
|
||||||
|
<item>Other reason</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
@ -7,4 +7,22 @@
|
|||||||
<item name="android:padding">12dp</item>
|
<item name="android:padding">12dp</item>
|
||||||
<!-- Add more style attributes as needed -->
|
<!-- Add more style attributes as needed -->
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="RoundedBorderStyle">
|
||||||
|
<!-- This style can be applied to views -->
|
||||||
|
<!-- <item name="android:background">@drawable/bg_button_outline</item>-->
|
||||||
|
<item name="strokeColor">@color/blue_500</item>
|
||||||
|
<item name="strokeWidth">2dp</item>
|
||||||
|
<item name="cornerRadius">8dp</item>
|
||||||
|
<item name="backgroundTint">@android:color/transparent</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="RoundedBorderStyleFilled">
|
||||||
|
<!-- This style can be applied to views -->
|
||||||
|
<!-- <item name="android:background">@drawable/bg_button_outline</item>-->
|
||||||
|
<item name="strokeColor">@color/blue_500</item>
|
||||||
|
<item name="strokeWidth">2dp</item>
|
||||||
|
<item name="cornerRadius">8dp</item>
|
||||||
|
<item name="backgroundTint">@color/blue_500</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
Reference in New Issue
Block a user