diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e47879a..92511bf 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -6,6 +6,8 @@
+
+
+
diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/AddEvidenceMultipartRequest.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/AddEvidenceMultipartRequest.kt
new file mode 100644
index 0000000..e6fcd2c
--- /dev/null
+++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/AddEvidenceMultipartRequest.kt
@@ -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
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/AddEvidenceRequest.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/AddEvidenceRequest.kt
index baee00c..48b97f8 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/AddEvidenceRequest.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/dto/AddEvidenceRequest.kt
@@ -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
)
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt b/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt
index e916206..b2e3971 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/data/api/retrofit/ApiService.kt
@@ -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
+ @Multipart
+ @POST("order/addevidence")
+ suspend fun addEvidenceMultipart(
+ @Part("order_id") orderId: RequestBody,
+ @Part("amount") amount: RequestBody,
+ @Part evidence: MultipartBody.Part
+ ): Response
+
@GET("order/{status}")
suspend fun getOrderList(
@Path("status") status: String
diff --git a/app/src/main/java/com/alya/ecommerce_serang/data/repository/OrderRepository.kt b/app/src/main/java/com/alya/ecommerce_serang/data/repository/OrderRepository.kt
index b1d8b7a..7a9381d 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/data/repository/OrderRepository.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/data/repository/OrderRepository.kt
@@ -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 {
- return try {
- Log.d("OrderRepository", "Add Evidence : $request")
- val response = apiService.addEvidence(request)
+// suspend fun uploadPaymentProof(request : AddEvidenceRequest): Result {
+// 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 {
+ 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 {
return try {
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/order/detail/AddEvidencePaymentActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/order/detail/AddEvidencePaymentActivity.kt
new file mode 100644
index 0000000..62dea9d
--- /dev/null
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/order/detail/AddEvidencePaymentActivity.kt
@@ -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,
+ 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
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/order/detail/PaymentActivity.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/order/detail/PaymentActivity.kt
index 00d3d49..fbe1010 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/ui/order/detail/PaymentActivity.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/order/detail/PaymentActivity.kt
@@ -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 = "-"
}
}
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/order/detail/PaymentViewModel.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/order/detail/PaymentViewModel.kt
index 5929f5a..4626ff0 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/ui/order/detail/PaymentViewModel.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/order/detail/PaymentViewModel.kt
@@ -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()
val error: LiveData get() = _error
+ private val _uploadResult = MutableLiveData>()
+ val uploadResult: LiveData> = _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)
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/alya/ecommerce_serang/ui/order/history/OrderHistoryAdapter.kt b/app/src/main/java/com/alya/ecommerce_serang/ui/order/history/OrderHistoryAdapter.kt
index 2258b10..45e3e6f 100644
--- a/app/src/main/java/com/alya/ecommerce_serang/ui/order/history/OrderHistoryAdapter.kt
+++ b/app/src/main/java/com/alya/ecommerce_serang/ui/order/history/OrderHistoryAdapter.kt
@@ -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(R.id.btn_right)
val statusOrder = itemView.findViewById(R.id.tvOrderStatus)
val deadlineLabel = itemView.findViewById(R.id.tvDeadlineLabel)
+ val deadlineDate = itemView.findViewById(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
+ }
}
}
}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/baseline_upload_file_24.xml b/app/src/main/res/drawable/baseline_upload_file_24.xml
new file mode 100644
index 0000000..6dc2d7a
--- /dev/null
+++ b/app/src/main/res/drawable/baseline_upload_file_24.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_add_evidence_payment.xml b/app/src/main/res/layout/activity_add_evidence_payment.xml
new file mode 100644
index 0000000..d253118
--- /dev/null
+++ b/app/src/main/res/layout/activity_add_evidence_payment.xml
@@ -0,0 +1,185 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 8249439..33d8c23 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -67,7 +67,7 @@
Tidak ada order
%d produk
%d produk lainnya
-
+
Semua Pesanan
Menunggu Tagihan
Konfrimasi Bayar
@@ -94,7 +94,4 @@
Beri Ulasan
-
-
-
\ No newline at end of file