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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +