mirror of
https://github.com/shaulascr/ecommerce_serang.git
synced 2025-08-10 17:32:22 +00:00
add sent evidence payment FIX
This commit is contained in:
@ -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" />
|
||||
|
@ -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
|
||||
)
|
@ -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
|
||||
)
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
||||
}
|
||||
}
|
@ -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 = "-"
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5
app/src/main/res/drawable/baseline_upload_file_24.xml
Normal file
5
app/src/main/res/drawable/baseline_upload_file_24.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#211E1E" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="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>
|
185
app/src/main/res/layout/activity_add_evidence_payment.xml
Normal file
185
app/src/main/res/layout/activity_add_evidence_payment.xml
Normal 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>
|
@ -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>
|
Reference in New Issue
Block a user