add sent evidence payment FIX

This commit is contained in:
shaulascr
2025-04-19 19:13:22 +07:00
parent d1fcec6d14
commit 2db0d2b27d
12 changed files with 737 additions and 55 deletions

View File

@ -6,6 +6,8 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32"/>
<application
android:allowBackup="true"
@ -19,6 +21,9 @@
android:theme="@style/Theme.Ecommerce_serang"
android:usesCleartextTraffic="true"
tools:targetApi="31">
<activity
android:name=".ui.order.detail.AddEvidencePaymentActivity"
android:exported="false" />
<activity
android:name=".ui.order.history.HistoryActivity"
android:exported="false" />

View File

@ -0,0 +1,10 @@
package com.alya.ecommerce_serang.data.api.dto
import okhttp3.MultipartBody
import okhttp3.RequestBody
data class AddEvidenceMultipartRequest(
val orderId: RequestBody,
val amount: RequestBody,
val evidence: MultipartBody.Part
)

View File

@ -1,7 +1,7 @@
package com.alya.ecommerce_serang.data.api.dto
import com.google.gson.annotations.SerializedName
import retrofit2.http.Multipart
import okhttp3.MultipartBody
data class AddEvidenceRequest (
@SerializedName("orer_id")
@ -11,5 +11,5 @@ data class AddEvidenceRequest (
val amount : String,
@SerializedName("evidence")
val evidence: Multipart
val evidence: MultipartBody.Part
)

View File

@ -33,14 +33,18 @@ 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.CreateAddressResponse
import com.alya.ecommerce_serang.data.api.response.profile.ProfileResponse
import okhttp3.MultipartBody
import okhttp3.RequestBody
import retrofit2.Call
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.Field
import retrofit2.http.FormUrlEncoded
import retrofit2.http.GET
import retrofit2.http.Multipart
import retrofit2.http.POST
import retrofit2.http.PUT
import retrofit2.http.Part
import retrofit2.http.Path
interface ApiService {
@ -99,6 +103,14 @@ interface ApiService {
@Body request : AddEvidenceRequest,
): Response<AddEvidenceResponse>
@Multipart
@POST("order/addevidence")
suspend fun addEvidenceMultipart(
@Part("order_id") orderId: RequestBody,
@Part("amount") amount: RequestBody,
@Part evidence: MultipartBody.Part
): Response<AddEvidenceResponse>
@GET("order/{status}")
suspend fun getOrderList(
@Path("status") status: String

View File

@ -1,7 +1,7 @@
package com.alya.ecommerce_serang.data.repository
import android.util.Log
import com.alya.ecommerce_serang.data.api.dto.AddEvidenceRequest
import com.alya.ecommerce_serang.data.api.dto.AddEvidenceMultipartRequest
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
@ -246,30 +246,59 @@ class OrderRepository(private val apiService: ApiService) {
}
}
suspend fun uploadPaymentProof(request : AddEvidenceRequest): Result<AddEvidenceResponse> {
return try {
Log.d("OrderRepository", "Add Evidence : $request")
val response = apiService.addEvidence(request)
// suspend fun uploadPaymentProof(request : AddEvidenceRequest): Result<AddEvidenceResponse> {
// return try {
// Log.d("OrderRepository", "Add Evidence : $request")
// val response = apiService.addEvidence(request)
//
// if (response.isSuccessful) {
// val addEvidenceResponse = response.body()
// if (addEvidenceResponse != null) {
// Log.d("OrderRepository", "Add Evidence successfully: ${addEvidenceResponse.message}")
// Result.Success(addEvidenceResponse)
// } else {
// Log.e("OrderRepository", "Response body was null")
// Result.Error(Exception("Empty response from server"))
// }
// } else {
// val errorBody = response.errorBody()?.string() ?: "Unknown error"
// Log.e("OrderRepository", "Error Add Evidence : $errorBody")
// Result.Error(Exception(errorBody))
// }
// } catch (e: Exception) {
// Log.e("OrderRepository", "Exception Add Evidence ", e)
// Result.Error(e)
// }
// }
suspend fun uploadPaymentProof(request: AddEvidenceMultipartRequest): Result<AddEvidenceResponse> {
return try {
Log.d("OrderRepository", "Uploading payment proof...")
if (response.isSuccessful) {
val addEvidenceResponse = response.body()
if (addEvidenceResponse != null) {
Log.d("OrderRepository", "Add Evidence successfully: ${addEvidenceResponse.message}")
Result.Success(addEvidenceResponse)
} else {
Log.e("OrderRepository", "Response body was null")
Result.Error(Exception("Empty response from server"))
}
val response = apiService.addEvidenceMultipart(
orderId = request.orderId,
amount = request.amount,
evidence = request.evidence
)
if (response.isSuccessful) {
val addEvidenceResponse = response.body()
if (addEvidenceResponse != null) {
Log.d("OrderRepository", "Payment proof uploaded successfully: ${addEvidenceResponse.message}")
Result.Success(addEvidenceResponse)
} else {
val errorBody = response.errorBody()?.string() ?: "Unknown error"
Log.e("OrderRepository", "Error Add Evidence : $errorBody")
Result.Error(Exception(errorBody))
Log.e("OrderRepository", "Response body was null")
Result.Error(Exception("Empty response from server"))
}
} catch (e: Exception) {
Log.e("OrderRepository", "Exception Add Evidence ", e)
Result.Error(e)
} else {
val errorBody = response.errorBody()?.string() ?: "Unknown error"
Log.e("OrderRepository", "Error uploading payment proof: $errorBody")
Result.Error(Exception(errorBody))
}
} catch (e: Exception) {
Log.e("OrderRepository", "Exception uploading payment proof", e)
Result.Error(e)
}
}
suspend fun getOrderList(status: String): Result<OrderListResponse> {
return try {

View File

@ -0,0 +1,340 @@
package com.alya.ecommerce_serang.ui.order.detail
import android.Manifest
import android.R
import android.app.DatePickerDialog
import android.content.pm.PackageManager
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.view.View
import android.webkit.MimeTypeMap
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.alya.ecommerce_serang.data.api.dto.AddEvidenceMultipartRequest
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.databinding.ActivityAddEvidencePaymentBinding
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
import com.alya.ecommerce_serang.utils.SessionManager
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import java.io.File
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
class AddEvidencePaymentActivity : AppCompatActivity() {
private lateinit var binding: ActivityAddEvidencePaymentBinding
private lateinit var sessionManager: SessionManager
private var orderId: Int = 0
private var paymentInfoId: Int = 0
private lateinit var productPrice: String
private var selectedImageUri: Uri? = null
private val viewModel: PaymentViewModel by viewModels {
BaseViewModelFactory {
val apiService = ApiConfig.getApiService(sessionManager)
val orderRepository = OrderRepository(apiService)
PaymentViewModel(orderRepository)
}
}
private val paymentMethods = arrayOf(
"Pilih metode pembayaran",
"Transfer Bank",
"E-Wallet",
"Virtual Account",
"Cash on Delivery"
)
private val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
uri?.let {
selectedImageUri = it
binding.ivUploadedImage.setImageURI(selectedImageUri)
binding.ivUploadedImage.visibility = View.VISIBLE
binding.layoutUploadPlaceholder.visibility = View.GONE
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityAddEvidencePaymentBinding.inflate(layoutInflater)
setContentView(binding.root)
sessionManager = SessionManager(this)
intent.extras?.let { bundle ->
orderId = bundle.getInt("ORDER_ID", 0)
paymentInfoId = bundle.getInt("PAYMENT_INFO_ID", 0)
productPrice = intent.getStringExtra("TOTAL_AMOUNT") ?: "Rp0"
}
setupUI()
viewModel.getOrderDetails(orderId)
setupListeners()
setupObservers()
}
private fun setupUI() {
// Set product details\
// Setup payment methods spinner
val adapter = ArrayAdapter(this, R.layout.simple_spinner_item, paymentMethods)
adapter.setDropDownViewResource(R.layout.simple_spinner_dropdown_item)
binding.spinnerPaymentMethod.adapter = adapter
}
private fun setupListeners() {
// Upload image button
binding.tvAddPhoto.setOnClickListener {
checkPermissionAndPickImage()
}
binding.frameUploadImage.setOnClickListener {
checkPermissionAndPickImage()
}
// Date picker
binding.tvPaymentDate.setOnClickListener {
showDatePicker()
}
// Payment method spinner
binding.spinnerPaymentMethod.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
// Skip the hint (first item)
if (position > 0) {
val selectedMethod = paymentMethods[position]
// You can also use it for further processing
Log.d(TAG, "Selected payment method: $selectedMethod")
}
}
override fun onNothingSelected(parent: AdapterView<*>?) {
// Do nothing
}
}
// Submit button
binding.btnSubmit.setOnClickListener {
validateAndUpload()
}
}
private fun setupObservers() {
viewModel.uploadResult.observe(this) { result ->
when (result) {
is com.alya.ecommerce_serang.data.repository.Result.Success -> {
Toast.makeText(this, "Bukti pembayaran berhasil dikirim", Toast.LENGTH_SHORT).show()
Log.d(TAG, "Upload successful: ${result.data}")
// Navigate back or to confirmation screen
finish()
}
is com.alya.ecommerce_serang.data.repository.Result.Error -> {
Log.e(TAG, "Upload failed: ${result.exception.message}")
Toast.makeText(this, "Gagal mengirim bukti pembayaran: ${result.exception.message}", Toast.LENGTH_SHORT).show()
}
is Result.Loading -> {
// Show loading indicator if needed
Log.d(TAG, "Uploading payment proof...")
}
}
}
}
private fun validateAndUpload() {
// Validate all fields
if (selectedImageUri == null) {
Toast.makeText(this, "Silahkan pilih bukti pembayaran", Toast.LENGTH_SHORT).show()
return
}
if (binding.spinnerPaymentMethod.selectedItemPosition == 0) {
Toast.makeText(this, "Silahkan pilih metode pembayaran", Toast.LENGTH_SHORT).show()
return
}
if (binding.etAccountNumber.text.toString().trim().isEmpty()) {
Toast.makeText(this, "Silahkan isi nomor rekening/HP", Toast.LENGTH_SHORT).show()
return
}
if (binding.tvPaymentDate.text.toString() == "Pilih tanggal") {
Toast.makeText(this, "Silahkan pilih tanggal pembayaran", Toast.LENGTH_SHORT).show()
return
}
// All validations passed, proceed with upload
uploadPaymentProof()
}
private fun uploadPaymentProof() {
selectedImageUri?.let { uri ->
// Convert URI to File
val file = getFileFromUri(uri)
file?.let {
try {
// Create MultipartBody.Part from File
val requestFile = file.asRequestBody("image/jpeg".toMediaTypeOrNull())
val evidencePart = MultipartBody.Part.createFormData("evidence", file.name, requestFile)
// Create RequestBody for order ID and amount
val orderIdPart = orderId.toString().toRequestBody("text/plain".toMediaTypeOrNull())
// Clean up the price string to get only the numeric value
val amountPart = productPrice.replace("Rp", "").replace(".", "").trim()
.toRequestBody("text/plain".toMediaTypeOrNull())
// Create the request object with the parts we need
val request = AddEvidenceMultipartRequest(
orderId = orderIdPart,
amount = amountPart,
evidence = evidencePart
)
// Log request details for debugging
Log.d(TAG, "Uploading payment proof - OrderID: $orderId, Amount: ${productPrice.replace("Rp", "").replace(".", "").trim()}")
Log.d(TAG, "File details - Name: ${file.name}, Size: ${file.length()} bytes, MIME: image/jpeg")
// Call the viewModel method
viewModel.uploadPaymentProof(request)
} catch (e: Exception) {
Log.e(TAG, "Error creating upload request: ${e.message}", e)
Toast.makeText(this, "Error preparing upload: ${e.message}", Toast.LENGTH_SHORT).show()
}
}
}
}
private fun getFileFromUri(uri: Uri): File? {
val contentResolver = applicationContext.contentResolver
val mimeType = contentResolver.getType(uri) ?: "image/jpeg"
val extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType) ?: "jpg"
Log.d("UploadEvidence", "URI: $uri")
Log.d("UploadEvidence", "Detected MIME type: $mimeType, extension: $extension")
// Ensure it's an image (either PNG, JPG, or JPEG)
if (mimeType != "image/png" && mimeType != "image/jpeg" && mimeType != "image/jpg") {
Log.e("UploadEvidence", "Invalid image MIME type: $mimeType. Only images are allowed.")
Toast.makeText(applicationContext, "Only image files are allowed", Toast.LENGTH_SHORT).show()
return null
}
try {
val inputStream = contentResolver.openInputStream(uri)
if (inputStream == null) {
Log.e("UploadEvidence", "Failed to open input stream from URI: $uri")
return null
}
// Create a temporary file with the correct extension
val tempFile = File.createTempFile("evidence_", ".$extension", cacheDir)
Log.d("UploadEvidence", "Temp file created at: ${tempFile.absolutePath}")
// Copy the content from inputStream to the temporary file
tempFile.outputStream().use { outputStream ->
inputStream.copyTo(outputStream)
inputStream.close()
}
// Verify if the file is a valid image
val bitmap = BitmapFactory.decodeFile(tempFile.absolutePath)
if (bitmap == null) {
Log.e("UploadEvidence", "File is not a valid image!")
tempFile.delete()
return null
} else {
bitmap.recycle() // Free memory
Log.d("UploadEvidence", "Valid image detected.")
}
Log.d("UploadEvidence", "File copied successfully. Size: ${tempFile.length()} bytes")
return tempFile
} catch (e: Exception) {
Log.e("UploadEvidence", "Error processing file: ${e.message}", e)
Toast.makeText(applicationContext, "Error processing image: ${e.message}", Toast.LENGTH_SHORT).show()
return null
}
}
private fun checkPermissionAndPickImage() {
val permission = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_IMAGES
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, arrayOf(permission), REQUEST_CODE_STORAGE_PERMISSION)
} else {
pickImage()
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_CODE_STORAGE_PERMISSION && grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
pickImage()
} else {
Toast.makeText(this, "Izin dibutuhkan untuk memilih gambar", Toast.LENGTH_SHORT).show()
}
}
private fun pickImage() {
getContent.launch("image/*")
}
private fun showDatePicker() {
val calendar = Calendar.getInstance()
val year = calendar.get(Calendar.YEAR)
val month = calendar.get(Calendar.MONTH)
val day = calendar.get(Calendar.DAY_OF_MONTH)
DatePickerDialog(
this,
{ _, selectedYear, selectedMonth, selectedDay ->
calendar.set(selectedYear, selectedMonth, selectedDay)
val sdf = SimpleDateFormat("dd-MM-yyyy", Locale.getDefault())
binding.tvPaymentDate.text = sdf.format(calendar.time)
},
year, month, day
).show()
}
companion object {
private const val PERMISSION_REQUEST_CODE = 100
private const val TAG = "AddEvidenceActivity"
private const val REQUEST_CODE_STORAGE_PERMISSION = 100
}
}

View File

@ -1,5 +1,6 @@
package com.alya.ecommerce_serang.ui.order.detail
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.Toast
@ -14,6 +15,7 @@ import com.alya.ecommerce_serang.utils.SessionManager
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
import java.util.TimeZone
class PaymentActivity : AppCompatActivity() {
private lateinit var binding: ActivityPaymentBinding
@ -64,11 +66,12 @@ class PaymentActivity : AppCompatActivity() {
// Setup button upload bukti bayar
binding.btnUploadPaymentProof.setOnClickListener {
// Intent ke activity upload bukti bayar
// val intent = Intent(this, UploadPaymentProofActivity::class.java)
// Intent ke activity upload bukti bayar
val intent = Intent(this, AddEvidencePaymentActivity::class.java)
intent.putExtra("ORDER_ID", orderId)
intent.putExtra("PAYMENT_INFO_ID", paymentInfoId)
Log.d(TAG, "Received Order ID: $orderId, Payment Info ID: $paymentInfoId")
intent.putExtra("TOTAL_AMOUNT", binding.tvTotalAmount.text.toString())
Log.d(TAG, "Received Order ID: $orderId, Payment Info ID: $paymentInfoId, Total Amount: ${binding.tvTotalAmount.text}")
startActivity(intent)
}
@ -127,9 +130,10 @@ class PaymentActivity : AppCompatActivity() {
Log.d(TAG, "Setting up payment due date from updated at: $createdAt")
try {
// Parse the created date
val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
val createdDate = dateFormat.parse(createdAt) ?: return
// Parse the ISO 8601 date
val isoDateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault())
isoDateFormat.timeZone = TimeZone.getTimeZone("UTC")
val createdDate = isoDateFormat.parse(createdAt) ?: return
// Add 24 hours to get due date
val calendar = Calendar.getInstance()
@ -139,10 +143,9 @@ class PaymentActivity : AppCompatActivity() {
// Format due date for display
val dueDateFormat = SimpleDateFormat("dd MMM yyyy", Locale.getDefault())
binding.tvDueDate.text = "Jatuh tempo ${dueDateFormat.format(dueDate)}"
binding.tvDueDate.text = "Jatuh tempo: ${dueDateFormat.format(dueDate)}"
Log.d(TAG, "Due Date: ${dueDateFormat.format(dueDate)}")
// Calculate remaining time
val now = Calendar.getInstance().time
val diff = dueDate.time - now.time
@ -152,12 +155,12 @@ class PaymentActivity : AppCompatActivity() {
val minutes = (diff % (60 * 60 * 1000)) / (60 * 1000)
binding.tvRemainingTime.text = "$hours jam $minutes menit"
Log.d(TAG, "Remaining Time: $hours hours $minutes minutes")
} else {
binding.tvRemainingTime.text = "Waktu habis"
}
} catch (e: Exception) {
binding.tvDueDate.text = "Jatuh tempo -"
Log.e(TAG, "Error parsing date", e)
binding.tvDueDate.text = "Jatuh tempo: -"
binding.tvRemainingTime.text = "-"
}
}

View File

@ -5,9 +5,12 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.alya.ecommerce_serang.data.api.dto.AddEvidenceMultipartRequest
import com.alya.ecommerce_serang.data.api.response.order.AddEvidenceResponse
import com.alya.ecommerce_serang.data.api.response.order.OrderListItemsItem
import com.alya.ecommerce_serang.data.api.response.order.Orders
import com.alya.ecommerce_serang.data.repository.OrderRepository
import com.alya.ecommerce_serang.data.repository.Result
import kotlinx.coroutines.launch
class PaymentViewModel(private val repository: OrderRepository) : ViewModel() {
@ -31,6 +34,9 @@ class PaymentViewModel(private val repository: OrderRepository) : ViewModel() {
private val _error = MutableLiveData<String>()
val error: LiveData<String> get() = _error
private val _uploadResult = MutableLiveData<Result<AddEvidenceResponse>>()
val uploadResult: LiveData<com.alya.ecommerce_serang.data.repository.Result<AddEvidenceResponse>> = _uploadResult
fun getOrderDetails(orderId: Int) {
_isLoading.value = true
viewModelScope.launch {
@ -50,4 +56,17 @@ class PaymentViewModel(private val repository: OrderRepository) : ViewModel() {
}
}
}
fun uploadPaymentProof(request: AddEvidenceMultipartRequest) {
viewModelScope.launch {
_uploadResult.value = com.alya.ecommerce_serang.data.repository.Result.Loading
try {
val result = repository.uploadPaymentProof(request)
_uploadResult.value = result
} catch (e: Exception) {
Log.e("PaymentProofViewModel", "Error uploading payment proof", e)
_uploadResult.value = com.alya.ecommerce_serang.data.repository.Result.Error(e)
}
}
}
}

View File

@ -12,6 +12,10 @@ import com.alya.ecommerce_serang.R
import com.alya.ecommerce_serang.data.api.response.order.OrdersItem
import com.alya.ecommerce_serang.ui.order.detail.PaymentActivity
import com.google.android.material.button.MaterialButton
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
import java.util.TimeZone
class OrderHistoryAdapter(
private val onOrderClickListener: (OrdersItem) -> Unit
@ -48,7 +52,7 @@ class OrderHistoryAdapter(
private val tvShowMore: TextView = itemView.findViewById(R.id.tvShowMore)
private val tvTotalAmount: TextView = itemView.findViewById(R.id.tvTotalAmount)
private val tvItemCountLabel: TextView = itemView.findViewById(R.id.tv_count_total_item)
private val tvDeadlineDate: TextView = itemView.findViewById(R.id.tvDeadlineDate)
// private val tvDeadlineDate: TextView = itemView.findViewById(R.id.tvDeadlineDate)
fun bind(order: OrdersItem) {
// Get store name from the first order item
@ -63,7 +67,7 @@ class OrderHistoryAdapter(
tvItemCountLabel.text = itemView.context.getString(R.string.item_count_prod, itemCount)
// Set deadline date, adjust to each status
tvDeadlineDate.text = formatDate(order.createdAt)
// tvDeadlineDate.text = formatDate(order.updatedAt)
// Set up the order items RecyclerView
val productAdapter = OrderProductAdapter()
@ -98,20 +102,6 @@ class OrderHistoryAdapter(
}
private fun getStatusLabel(status: String): String {
return when (status.toLowerCase()) {
"pending" -> itemView.context.getString(R.string.pending_orders)
"unpaid" -> itemView.context.getString(R.string.unpaid_orders)
"processed" -> itemView.context.getString(R.string.processed_orders)
"paid" -> itemView.context.getString(R.string.paid_orders)
"shipped" -> itemView.context.getString(R.string.shipped_orders)
"delivered" -> itemView.context.getString(R.string.delivered_orders)
"completed" -> itemView.context.getString(R.string.completed_orders)
"canceled" -> itemView.context.getString(R.string.canceled_orders)
else -> status
}
}
private fun adjustButtonsAndText(status: String, order: OrdersItem) {
Log.d("OrderHistoryAdapter", "Adjusting buttons for status: $status")
// Mendapatkan referensi ke tombol-tombol
@ -119,6 +109,7 @@ class OrderHistoryAdapter(
val btnRight = itemView.findViewById<MaterialButton>(R.id.btn_right)
val statusOrder = itemView.findViewById<TextView>(R.id.tvOrderStatus)
val deadlineLabel = itemView.findViewById<TextView>(R.id.tvDeadlineLabel)
val deadlineDate = itemView.findViewById<TextView>(R.id.tvDeadlineDate)
// Reset visibility
btnLeft.visibility = View.GONE
@ -136,6 +127,10 @@ class OrderHistoryAdapter(
visibility = View.VISIBLE
text = itemView.context.getString(R.string.dl_pending)
}
deadlineDate.apply {
visibility = View.VISIBLE
text = formatDate(order.createdAt)
}
}
"unpaid" -> {
statusOrder.apply {
@ -152,6 +147,7 @@ class OrderHistoryAdapter(
setOnClickListener {
}
}
btnRight.apply {
visibility = View.VISIBLE
text = itemView.context.getString(R.string.sent_evidence)
@ -165,6 +161,10 @@ class OrderHistoryAdapter(
itemView.context.startActivity(intent)
}
}
deadlineDate.apply {
visibility = View.VISIBLE
text = formatDatePay(order.updatedAt)
}
}
"processed" -> {
// Untuk status processed, tampilkan "Hubungi Penjual"
@ -202,6 +202,10 @@ class OrderHistoryAdapter(
// Handle click event
}
}
deadlineDate.apply {
visibility = View.VISIBLE
text = formatShipmentDate(order.updatedAt, order.etd.toInt())
}
}
"delivered" -> {
// Untuk status delivered, tampilkan "Beri Ulasan"
@ -230,13 +234,86 @@ class OrderHistoryAdapter(
}
}
}
"canceled" -> {
statusOrder.apply {
visibility = View.VISIBLE
text = itemView.context.getString(R.string.canceled_orders)
}
}
}
}
private fun formatDate(dateString: String): String {
// In a real app, you would parse the date string and format it
// For this example, just return the string as is
return dateString
return try {
val inputFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault())
inputFormat.timeZone = TimeZone.getTimeZone("UTC")
val outputFormat = SimpleDateFormat("HH:mm dd MMMM yyyy", Locale("id", "ID"))
val date = inputFormat.parse(dateString)
date?.let {
val calendar = Calendar.getInstance()
calendar.time = it
calendar.set(Calendar.HOUR_OF_DAY, 23)
calendar.set(Calendar.MINUTE, 59)
outputFormat.format(calendar.time)
} ?: dateString
} catch (e: Exception) {
Log.e("DateFormatting", "Error formatting date: ${e.message}")
dateString
}
}
private fun formatDatePay(dateString: String): String {
return try {
// Parse the ISO 8601 date
val isoDateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault())
isoDateFormat.timeZone = TimeZone.getTimeZone("UTC")
val createdDate = isoDateFormat.parse(dateString)
// Add 24 hours to get due date
val calendar = Calendar.getInstance()
calendar.time = createdDate
calendar.add(Calendar.HOUR, 24)
val dueDate = calendar.time
// Format due date for display
val dueDateFormat = SimpleDateFormat("dd MMM yyyy", Locale.getDefault())
dueDateFormat.format(calendar.time)
} catch (e: Exception) {
Log.e("DateFormatting", "Error formatting date: ${e.message}")
dateString
}
}
private fun formatShipmentDate(dateString: String, estimate: Int): String {
return try {
// Parse the input date
val inputFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault())
inputFormat.timeZone = TimeZone.getTimeZone("UTC")
// Output format
val outputFormat = SimpleDateFormat("dd MMMM yyyy", Locale("id", "ID"))
// Parse the input date
val date = inputFormat.parse(dateString)
date?.let {
val calendar = Calendar.getInstance()
calendar.time = it
// Add estimated days
calendar.add(Calendar.DAY_OF_MONTH, estimate)
outputFormat.format(calendar.time)
} ?: dateString
} catch (e: Exception) {
Log.e("ShipmentDateFormatting", "Error formatting shipment date: ${e.message}")
dateString
}
}
}
}

View 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="M14,2L6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2L18,22c1.1,0 2,-0.9 2,-2L20,8l-6,-6zM18,20L6,20L6,4h7v5h5v11zM8,15.01l1.41,1.41L11,14.84L11,19h2v-4.16l1.59,1.59L16,15.01 12.01,11z"/>
</vector>

View File

@ -0,0 +1,185 @@
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.order.detail.AddEvidencePaymentActivity">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#FFFFFF"
app:navigationIcon="@drawable/ic_back_24"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="Kirim Bukti Bayar"
android:textColor="#000000"
android:textSize="18sp"
android:layout_marginVertical="8dp"
android:fontFamily="@font/dmsans_bold" />
</androidx.appcompat.widget.Toolbar>
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#EEEEEE"
app:layout_constraintTop_toBottomOf="@id/toolbar" />
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/divider">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Unggah Foto *"
android:fontFamily="@font/dmsans_semibold"
android:textSize="16sp" />
<androidx.cardview.widget.CardView
android:id="@+id/cardAddPhoto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:cardCornerRadius="8dp"
app:cardElevation="0dp"
app:cardBackgroundColor="#FFFFFF">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp">
<TextView
android:id="@+id/tvAddPhoto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tambah Foto"
android:textColor="#1E88E5"
android:textSize="14sp" />
<FrameLayout
android:id="@+id/frameUploadImage"
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_marginTop="8dp"
android:background="@drawable/edit_text_background"
android:padding="8dp">
<ImageView
android:id="@+id/ivUploadedImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:visibility="gone" />
<LinearLayout
android:id="@+id/layoutUploadPlaceholder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/edit_text_background"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/baseline_upload_file_24" />
</LinearLayout>
</FrameLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Metode Pembayaran *"
android:fontFamily="@font/dmsans_semibold"
android:textSize="16sp" />
<Spinner
android:id="@+id/spinnerPaymentMethod"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:background="@drawable/edit_text_background"
android:minHeight="50dp"
android:padding="12dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Nomor Rekening / Nomor HP *"
android:fontFamily="@font/dmsans_semibold"
android:textSize="16sp" />
<EditText
android:id="@+id/etAccountNumber"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:background="@drawable/edit_text_background"
android:hint="Isi nomor rekening atau nomor hp pembayaran"
android:inputType="text"
android:minHeight="50dp"
android:textSize="14sp"
android:padding="12dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Tanggal Pembayaran *"
android:fontFamily="@font/dmsans_semibold"
android:textSize="16sp" />
<TextView
android:id="@+id/tvPaymentDate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:background="@drawable/edit_text_background"
android:drawableEnd="@drawable/ic_calendar"
android:drawablePadding="8dp"
android:hint="Pilih tanggal"
android:minHeight="50dp"
android:padding="12dp" />
<Button
android:id="@+id/btnSubmit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginBottom="16dp"
android:background="@drawable/bg_button_filled"
android:text="Kirim"
android:textAllCaps="false"
android:textColor="#FFFFFF"
android:textSize="16sp"
android:padding="12dp" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -67,7 +67,7 @@
<string name="no_available_orders">Tidak ada order</string>
<string name="item_count_prod">%d produk</string>
<string name="show_more_product">%d produk lainnya</string>
<!-- status order-->
<!-- status order-->
<string name="all_orders">Semua Pesanan </string>
<string name="pending_orders">Menunggu Tagihan</string>
<string name="unpaid_orders">Konfrimasi Bayar</string>
@ -94,7 +94,4 @@
<string name="add_review">Beri Ulasan </string>
</resources>