memperbaiki list cetak ulang
This commit is contained in:
		
							parent
							
								
									4aaa9957e7
								
							
						
					
					
						commit
						b0ee2e8ee6
					
				
							
								
								
									
										106
									
								
								app/src/main/java/com/example/bdkipoc/StyleHelper.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								app/src/main/java/com/example/bdkipoc/StyleHelper.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,106 @@
 | 
				
			|||||||
 | 
					package com.example.bdkipoc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.content.Context;
 | 
				
			||||||
 | 
					import android.graphics.drawable.GradientDrawable;
 | 
				
			||||||
 | 
					import android.view.View;
 | 
				
			||||||
 | 
					import android.view.ViewGroup;
 | 
				
			||||||
 | 
					import android.widget.TextView;
 | 
				
			||||||
 | 
					import androidx.core.content.ContextCompat;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class StyleHelper {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Create rounded rectangle drawable programmatically
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static GradientDrawable createRoundedDrawable(int color, int strokeColor, int strokeWidth, int radius) {
 | 
				
			||||||
 | 
					        GradientDrawable drawable = new GradientDrawable();
 | 
				
			||||||
 | 
					        drawable.setShape(GradientDrawable.RECTANGLE);
 | 
				
			||||||
 | 
					        drawable.setColor(color);
 | 
				
			||||||
 | 
					        drawable.setStroke(strokeWidth, strokeColor);
 | 
				
			||||||
 | 
					        drawable.setCornerRadius(radius);
 | 
				
			||||||
 | 
					        return drawable;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Apply search input styling
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static void applySearchInputStyle(View view, Context context) {
 | 
				
			||||||
 | 
					        int white = ContextCompat.getColor(context, android.R.color.white);
 | 
				
			||||||
 | 
					        int lightGrey = ContextCompat.getColor(context, android.R.color.darker_gray);
 | 
				
			||||||
 | 
					        // ✅ IMPROVED: Larger corner radius and lighter border like in the image
 | 
				
			||||||
 | 
					        GradientDrawable drawable = createRoundedDrawable(white, lightGrey, 1, 75); // 25dp radius, thinner border
 | 
				
			||||||
 | 
					        view.setBackground(drawable);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Apply filter button styling
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static void applyFilterButtonStyle(View view, Context context) {
 | 
				
			||||||
 | 
					        int white = ContextCompat.getColor(context, android.R.color.white);
 | 
				
			||||||
 | 
					        int lightGrey = ContextCompat.getColor(context, android.R.color.darker_gray);
 | 
				
			||||||
 | 
					        // ✅ IMPROVED: Larger corner radius like in the image
 | 
				
			||||||
 | 
					        GradientDrawable drawable = createRoundedDrawable(white, lightGrey, 1, 75); // 25dp radius, thinner border
 | 
				
			||||||
 | 
					        view.setBackground(drawable);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Apply pagination button styling (simple version)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static void applyPaginationButtonStyle(View view, Context context, boolean isActive) {
 | 
				
			||||||
 | 
					        int backgroundColor, strokeColor;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (isActive) {
 | 
				
			||||||
 | 
					            backgroundColor = ContextCompat.getColor(context, android.R.color.holo_red_dark);
 | 
				
			||||||
 | 
					            strokeColor = ContextCompat.getColor(context, android.R.color.holo_red_dark);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            backgroundColor = ContextCompat.getColor(context, android.R.color.white);
 | 
				
			||||||
 | 
					            strokeColor = ContextCompat.getColor(context, android.R.color.transparent);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // ✅ IMPROVED: Larger corner radius for modern look (like in the image)
 | 
				
			||||||
 | 
					        GradientDrawable drawable = createRoundedDrawable(backgroundColor, strokeColor, 0, 48); // 16dp radius
 | 
				
			||||||
 | 
					        view.setBackground(drawable);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Set text color if it's a TextView
 | 
				
			||||||
 | 
					        if (view instanceof TextView) {
 | 
				
			||||||
 | 
					            int textColor = isActive ? 
 | 
				
			||||||
 | 
					                ContextCompat.getColor(context, android.R.color.white) : 
 | 
				
			||||||
 | 
					                ContextCompat.getColor(context, android.R.color.black);
 | 
				
			||||||
 | 
					            ((TextView) view).setTextColor(textColor);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Apply status text color only (no background badge)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static void applyStatusTextColor(TextView textView, Context context, String status) {
 | 
				
			||||||
 | 
					        String statusLower = status != null ? status.toLowerCase() : "";
 | 
				
			||||||
 | 
					        int textColor;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (statusLower.equals("failed") || statusLower.equals("failure") || 
 | 
				
			||||||
 | 
					            statusLower.equals("error") || statusLower.equals("declined") || 
 | 
				
			||||||
 | 
					            statusLower.equals("expire") || statusLower.equals("cancel")) {
 | 
				
			||||||
 | 
					            // Red text for failed/error statuses
 | 
				
			||||||
 | 
					            textColor = ContextCompat.getColor(context, android.R.color.holo_red_dark);
 | 
				
			||||||
 | 
					        } else if (statusLower.equals("success") || statusLower.equals("paid") || 
 | 
				
			||||||
 | 
					                statusLower.equals("settlement") || statusLower.equals("completed") ||
 | 
				
			||||||
 | 
					                statusLower.equals("capture")) {
 | 
				
			||||||
 | 
					            // Green text for successful statuses
 | 
				
			||||||
 | 
					            textColor = ContextCompat.getColor(context, android.R.color.holo_green_dark);
 | 
				
			||||||
 | 
					        } else if (statusLower.equals("pending") || statusLower.equals("processing") || 
 | 
				
			||||||
 | 
					                statusLower.equals("waiting") || statusLower.equals("checking...") || 
 | 
				
			||||||
 | 
					                statusLower.equals("checking")) {
 | 
				
			||||||
 | 
					            // Orange text for pending/processing statuses
 | 
				
			||||||
 | 
					            textColor = ContextCompat.getColor(context, android.R.color.holo_orange_dark);
 | 
				
			||||||
 | 
					        } else if (statusLower.equals("init")) {
 | 
				
			||||||
 | 
					            // Blue text for init status
 | 
				
			||||||
 | 
					            textColor = ContextCompat.getColor(context, android.R.color.holo_blue_dark);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // Default gray text for unknown statuses
 | 
				
			||||||
 | 
					            textColor = ContextCompat.getColor(context, android.R.color.darker_gray);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        textView.setTextColor(textColor);
 | 
				
			||||||
 | 
					        textView.setBackground(null); // Remove any background
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -11,6 +11,8 @@ import android.widget.EditText;
 | 
				
			|||||||
import android.widget.ImageButton;
 | 
					import android.widget.ImageButton;
 | 
				
			||||||
import android.widget.ProgressBar;
 | 
					import android.widget.ProgressBar;
 | 
				
			||||||
import android.widget.Toast;
 | 
					import android.widget.Toast;
 | 
				
			||||||
 | 
					import android.widget.TextView;
 | 
				
			||||||
 | 
					import android.widget.LinearLayout;
 | 
				
			||||||
import android.content.Intent;
 | 
					import android.content.Intent;
 | 
				
			||||||
import android.util.Log;
 | 
					import android.util.Log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -50,16 +52,25 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
    private EditText searchEditText;
 | 
					    private EditText searchEditText;
 | 
				
			||||||
    private ImageButton searchButton;
 | 
					    private ImageButton searchButton;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    // ✅ PAGINATION UI ELEMENTS
 | 
				
			||||||
 | 
					    private LinearLayout infoBar;
 | 
				
			||||||
 | 
					    private TextView textTotalRecords;
 | 
				
			||||||
 | 
					    private TextView textPageInfo;
 | 
				
			||||||
 | 
					    private LinearLayout paginationControls;
 | 
				
			||||||
 | 
					    private LinearLayout pageNumbersContainer;
 | 
				
			||||||
 | 
					    private ImageButton btnFirstPage, btnPrevPage, btnNextPage, btnLastPage;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    // ✅ FRONTEND DEDUPLICATION: Local caching and tracking
 | 
					    // ✅ FRONTEND DEDUPLICATION: Local caching and tracking
 | 
				
			||||||
    private Map<String, Transaction> transactionCache = new HashMap<>();
 | 
					    private Map<String, Transaction> transactionCache = new HashMap<>();
 | 
				
			||||||
    private Set<String> processedReferences = new HashSet<>();
 | 
					    private Set<String> processedReferences = new HashSet<>();
 | 
				
			||||||
    private SharedPreferences prefs;
 | 
					    private SharedPreferences prefs;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    // Pagination variables
 | 
					    // ✅ UPDATED PAGINATION VARIABLES
 | 
				
			||||||
    private int page = 0;
 | 
					    private int currentPage = 1; // Start from page 1 instead of 0
 | 
				
			||||||
    private final int limit = 50; // ✅ INCREASED: Fetch more data for better deduplication
 | 
					    private final int itemsPerPage = 15; // ✅ 15 items per page as requested
 | 
				
			||||||
 | 
					    private int totalRecords = 0;
 | 
				
			||||||
 | 
					    private int totalPages = 0;
 | 
				
			||||||
    private boolean isLoading = false;
 | 
					    private boolean isLoading = false;
 | 
				
			||||||
    private boolean isLastPage = false;
 | 
					 | 
				
			||||||
    private String currentSearchQuery = "";
 | 
					    private String currentSearchQuery = "";
 | 
				
			||||||
    private boolean isRefreshing = false;
 | 
					    private boolean isRefreshing = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -83,15 +94,38 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
        // Setup search functionality
 | 
					        // Setup search functionality
 | 
				
			||||||
        setupSearch();
 | 
					        setupSearch();
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
 | 
					        // ✅ Setup pagination controls
 | 
				
			||||||
 | 
					        setupPaginationControls();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        // Load initial data
 | 
					        // Load initial data
 | 
				
			||||||
        loadTransactions(0);
 | 
					        loadTransactions(1); // Start from page 1
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void initViews() {
 | 
					    private void initViews() {
 | 
				
			||||||
        recyclerView = findViewById(R.id.recyclerView);
 | 
					        recyclerView = findViewById(R.id.recyclerView);
 | 
				
			||||||
        progressBar = findViewById(R.id.progressBar);
 | 
					        progressBar = findViewById(R.id.progressBar);
 | 
				
			||||||
        searchEditText = findViewById(R.id.searchEditText);
 | 
					        searchEditText = findViewById(R.id.searchEditText);
 | 
				
			||||||
        searchButton = findViewById(R.id.searchButton);
 | 
					        
 | 
				
			||||||
 | 
					        // ✅ APPLY PROGRAMMATIC STYLING
 | 
				
			||||||
 | 
					        LinearLayout searchContainer = findViewById(R.id.searchContainer);
 | 
				
			||||||
 | 
					        LinearLayout filterButton = findViewById(R.id.filterButton);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        StyleHelper.applySearchInputStyle(searchContainer, this);
 | 
				
			||||||
 | 
					        StyleHelper.applyFilterButtonStyle(filterButton, this);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // ✅ PAGINATION UI ELEMENTS
 | 
				
			||||||
 | 
					        paginationControls = findViewById(R.id.paginationControls);
 | 
				
			||||||
 | 
					        pageNumbersContainer = findViewById(R.id.pageNumbersContainer);
 | 
				
			||||||
 | 
					        btnFirstPage = findViewById(R.id.btnFirstPage);
 | 
				
			||||||
 | 
					        btnPrevPage = findViewById(R.id.btnPrevPage);
 | 
				
			||||||
 | 
					        btnNextPage = findViewById(R.id.btnNextPage);
 | 
				
			||||||
 | 
					        btnLastPage = findViewById(R.id.btnLastPage);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Apply pagination button styling (updated sizes)
 | 
				
			||||||
 | 
					        StyleHelper.applyPaginationButtonStyle(btnFirstPage, this, false);
 | 
				
			||||||
 | 
					        StyleHelper.applyPaginationButtonStyle(btnPrevPage, this, false);
 | 
				
			||||||
 | 
					        StyleHelper.applyPaginationButtonStyle(btnNextPage, this, false);
 | 
				
			||||||
 | 
					        StyleHelper.applyPaginationButtonStyle(btnLastPage, this, false);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void setupToolbar() {
 | 
					    private void setupToolbar() {
 | 
				
			||||||
@ -114,32 +148,12 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
        recyclerView.setLayoutManager(layoutManager);
 | 
					        recyclerView.setLayoutManager(layoutManager);
 | 
				
			||||||
        recyclerView.setAdapter(adapter);
 | 
					        recyclerView.setAdapter(adapter);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Add scroll listener for pagination
 | 
					        // ✅ REMOVED: Auto-pagination scroll listener since we use manual pagination now
 | 
				
			||||||
        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
 | 
					        // Manual pagination is better for user control and performance
 | 
				
			||||||
            @Override
 | 
					 | 
				
			||||||
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
 | 
					 | 
				
			||||||
                super.onScrolled(recyclerView, dx, dy);
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                // Pagination: Load more when reaching the bottom
 | 
					 | 
				
			||||||
                if (!recyclerView.canScrollVertically(1) && !isLoading && !isLastPage && currentSearchQuery.isEmpty()) {
 | 
					 | 
				
			||||||
                    loadTransactions(page + 1);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void setupSearch() {
 | 
					    private void setupSearch() {
 | 
				
			||||||
        // Search button click listener
 | 
					        // Search on text change
 | 
				
			||||||
        searchButton.setOnClickListener(v -> performSearch());
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Search button long press for refresh
 | 
					 | 
				
			||||||
        searchButton.setOnLongClickListener(v -> {
 | 
					 | 
				
			||||||
            refreshTransactions();
 | 
					 | 
				
			||||||
            Toast.makeText(this, "Refreshing data...", Toast.LENGTH_SHORT).show();
 | 
					 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Search EditText listener
 | 
					 | 
				
			||||||
        searchEditText.addTextChangedListener(new TextWatcher() {
 | 
					        searchEditText.addTextChangedListener(new TextWatcher() {
 | 
				
			||||||
            @Override
 | 
					            @Override
 | 
				
			||||||
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
 | 
					            public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
 | 
				
			||||||
@ -147,12 +161,89 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
            @Override
 | 
					            @Override
 | 
				
			||||||
            public void onTextChanged(CharSequence s, int start, int before, int count) {
 | 
					            public void onTextChanged(CharSequence s, int start, int before, int count) {
 | 
				
			||||||
                currentSearchQuery = s.toString().trim();
 | 
					                currentSearchQuery = s.toString().trim();
 | 
				
			||||||
 | 
					                // ✅ RESET TO PAGE 1 when searching
 | 
				
			||||||
 | 
					                currentPage = 1;
 | 
				
			||||||
                filterTransactions(currentSearchQuery);
 | 
					                filterTransactions(currentSearchQuery);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            @Override
 | 
					            @Override
 | 
				
			||||||
            public void afterTextChanged(Editable s) {}
 | 
					            public void afterTextChanged(Editable s) {}
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Filter button click
 | 
				
			||||||
 | 
					        findViewById(R.id.filterButton).setOnClickListener(v -> {
 | 
				
			||||||
 | 
					            Toast.makeText(this, "Filter clicked", Toast.LENGTH_SHORT).show();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ✅ NEW METHOD: Setup pagination controls
 | 
				
			||||||
 | 
					    private void setupPaginationControls() {
 | 
				
			||||||
 | 
					        btnFirstPage.setOnClickListener(v -> {
 | 
				
			||||||
 | 
					            if (currentPage > 1) {
 | 
				
			||||||
 | 
					                goToPage(1);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        btnPrevPage.setOnClickListener(v -> {
 | 
				
			||||||
 | 
					            if (currentPage > 1) {
 | 
				
			||||||
 | 
					                goToPage(currentPage - 1);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        btnNextPage.setOnClickListener(v -> {
 | 
				
			||||||
 | 
					            if (currentPage < totalPages) {
 | 
				
			||||||
 | 
					                goToPage(currentPage + 1);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        btnLastPage.setOnClickListener(v -> {
 | 
				
			||||||
 | 
					            if (currentPage < totalPages) {
 | 
				
			||||||
 | 
					                goToPage(totalPages);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ✅ NEW METHOD: Navigate to specific page
 | 
				
			||||||
 | 
					    private void goToPage(int page) {
 | 
				
			||||||
 | 
					        if (page < 1 || page > totalPages || page == currentPage || isLoading) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Log.d("TransactionActivity", "🔄 Navigating to page " + page);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (currentSearchQuery.isEmpty()) {
 | 
				
			||||||
 | 
					            // Load from API
 | 
				
			||||||
 | 
					            loadTransactions(page);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // Search mode - just update current page and refresh display
 | 
				
			||||||
 | 
					            currentPage = page;
 | 
				
			||||||
 | 
					            updatePaginationDisplay();
 | 
				
			||||||
 | 
					            displayCurrentPageData();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ✅ NEW METHOD: Display current page data for search results
 | 
				
			||||||
 | 
					    private void displayCurrentPageData() {
 | 
				
			||||||
 | 
					        if (currentSearchQuery.isEmpty()) {
 | 
				
			||||||
 | 
					            return; // This method is only for search results
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        int startIndex = (currentPage - 1) * itemsPerPage;
 | 
				
			||||||
 | 
					        int endIndex = Math.min(startIndex + itemsPerPage, filteredList.size());
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        List<Transaction> pageData = new ArrayList<>();
 | 
				
			||||||
 | 
					        for (int i = startIndex; i < endIndex; i++) {
 | 
				
			||||||
 | 
					            pageData.add(filteredList.get(i));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Update adapter with current page data
 | 
				
			||||||
 | 
					        adapter.updateData(pageData, startIndex); // Pass startIndex for numbering
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Scroll to top
 | 
				
			||||||
 | 
					        recyclerView.scrollToPosition(0);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Log.d("TransactionActivity", "📄 Displaying search results page " + currentPage + 
 | 
				
			||||||
 | 
					            " (items " + (startIndex + 1) + "-" + endIndex + " of " + filteredList.size() + ")");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void refreshTransactions() {
 | 
					    private void refreshTransactions() {
 | 
				
			||||||
@ -166,17 +257,17 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
        // Clear search when refreshing
 | 
					        // Clear search when refreshing
 | 
				
			||||||
        searchEditText.setText("");
 | 
					        searchEditText.setText("");
 | 
				
			||||||
        currentSearchQuery = "";
 | 
					        currentSearchQuery = "";
 | 
				
			||||||
        page = 0;
 | 
					        currentPage = 1; // ✅ Reset to page 1
 | 
				
			||||||
        isLastPage = false;
 | 
					 | 
				
			||||||
        transactionList.clear();
 | 
					        transactionList.clear();
 | 
				
			||||||
        filteredList.clear();
 | 
					        filteredList.clear();
 | 
				
			||||||
        adapter.notifyDataSetChanged();
 | 
					        adapter.notifyDataSetChanged();
 | 
				
			||||||
        loadTransactions(0);
 | 
					        loadTransactions(1);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void performSearch() {
 | 
					    private void performSearch() {
 | 
				
			||||||
        String query = searchEditText.getText().toString().trim();
 | 
					        String query = searchEditText.getText().toString().trim();
 | 
				
			||||||
        currentSearchQuery = query;
 | 
					        currentSearchQuery = query;
 | 
				
			||||||
 | 
					        currentPage = 1; // ✅ Reset to page 1 when searching
 | 
				
			||||||
        filterTransactions(query);
 | 
					        filterTransactions(query);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // Hide keyboard
 | 
					        // Hide keyboard
 | 
				
			||||||
@ -187,17 +278,53 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
        filteredList.clear();
 | 
					        filteredList.clear();
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        if (query.isEmpty()) {
 | 
					        if (query.isEmpty()) {
 | 
				
			||||||
 | 
					            // ✅ NO SEARCH: Show current API page data (already sorted)
 | 
				
			||||||
            filteredList.addAll(transactionList);
 | 
					            filteredList.addAll(transactionList);
 | 
				
			||||||
 | 
					            totalRecords = totalRecords; // Use API total
 | 
				
			||||||
 | 
					            totalPages = (int) Math.ceil((double) totalRecords / itemsPerPage);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // ✅ VERIFY FILTERED LIST ORDER
 | 
				
			||||||
 | 
					            Log.d("TransactionActivity", "📋 FILTERED LIST ORDER (no search):");
 | 
				
			||||||
 | 
					            for (int i = 0; i < Math.min(5, filteredList.size()); i++) {
 | 
				
			||||||
 | 
					                Transaction tx = filteredList.get(i);
 | 
				
			||||||
 | 
					                Log.d("TransactionActivity", "   " + (i+1) + ". " + tx.createdAt + " - " + tx.referenceId);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
 | 
					            // ✅ SEARCH MODE: Filter all available data
 | 
				
			||||||
            for (Transaction transaction : transactionList) {
 | 
					            for (Transaction transaction : transactionList) {
 | 
				
			||||||
                if (transaction.referenceId.toLowerCase().contains(query.toLowerCase()) ||
 | 
					                if (transaction.referenceId.toLowerCase().contains(query.toLowerCase()) ||
 | 
				
			||||||
                    transaction.amount.contains(query)) {
 | 
					                    transaction.amount.contains(query)) {
 | 
				
			||||||
                    filteredList.add(transaction);
 | 
					                    filteredList.add(transaction);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // ✅ SORT SEARCH RESULTS by date
 | 
				
			||||||
 | 
					            filteredList.sort((t1, t2) -> {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    Date date1 = parseCreatedAtDate(t1.createdAt);
 | 
				
			||||||
 | 
					                    Date date2 = parseCreatedAtDate(t2.createdAt);
 | 
				
			||||||
 | 
					                    if (date1 != null && date2 != null) {
 | 
				
			||||||
 | 
					                        return date2.compareTo(date1); // Newest first
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } catch (Exception e) {
 | 
				
			||||||
 | 
					                    // Fallback
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return Integer.compare(t2.id, t1.id);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // ✅ SEARCH PAGINATION: Calculate pages for filtered results
 | 
				
			||||||
 | 
					            totalRecords = filteredList.size();
 | 
				
			||||||
 | 
					            totalPages = (int) Math.ceil((double) totalRecords / itemsPerPage);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // ✅ Display current page of search results
 | 
				
			||||||
 | 
					            displayCurrentPageData();
 | 
				
			||||||
 | 
					            updatePaginationDisplay();
 | 
				
			||||||
 | 
					            return; // Early return for search
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        adapter.notifyDataSetChanged();
 | 
					        // ✅ For non-search, just update adapter normally
 | 
				
			||||||
 | 
					        adapter.updateData(filteredList, (currentPage - 1) * itemsPerPage);
 | 
				
			||||||
 | 
					        updatePaginationDisplay();
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // Scroll to top after filtering
 | 
					        // Scroll to top after filtering
 | 
				
			||||||
        if (!filteredList.isEmpty()) {
 | 
					        if (!filteredList.isEmpty()) {
 | 
				
			||||||
@ -205,18 +332,104 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ✅ NEW METHOD: Update pagination display
 | 
				
			||||||
 | 
					    private void updatePaginationDisplay() {
 | 
				
			||||||
 | 
					        // Update page info - remove total records display for cleaner look
 | 
				
			||||||
 | 
					        String pageText = "Halaman " + currentPage + " dari " + Math.max(1, totalPages);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Show/hide pagination controls based on data availability
 | 
				
			||||||
 | 
					        if (totalRecords > 0) {
 | 
				
			||||||
 | 
					            if (totalPages > 1) {
 | 
				
			||||||
 | 
					                paginationControls.setVisibility(View.VISIBLE);
 | 
				
			||||||
 | 
					                updatePageButtons();
 | 
				
			||||||
 | 
					                createPageNumbers();
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                paginationControls.setVisibility(View.GONE);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            paginationControls.setVisibility(View.GONE);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Log.d("TransactionActivity", "📊 Pagination updated: " + 
 | 
				
			||||||
 | 
					            "Page " + currentPage + "/" + totalPages + ", Total: " + totalRecords);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ✅ NEW METHOD: Update pagination button states
 | 
				
			||||||
 | 
					    private void updatePageButtons() {
 | 
				
			||||||
 | 
					        // Enable/disable buttons based on current page
 | 
				
			||||||
 | 
					        btnFirstPage.setEnabled(currentPage > 1);
 | 
				
			||||||
 | 
					        btnPrevPage.setEnabled(currentPage > 1);
 | 
				
			||||||
 | 
					        btnNextPage.setEnabled(currentPage < totalPages);
 | 
				
			||||||
 | 
					        btnLastPage.setEnabled(currentPage < totalPages);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Update button opacity for visual feedback
 | 
				
			||||||
 | 
					        float enabledAlpha = 1.0f;
 | 
				
			||||||
 | 
					        float disabledAlpha = 0.3f;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        btnFirstPage.setAlpha(btnFirstPage.isEnabled() ? enabledAlpha : disabledAlpha);
 | 
				
			||||||
 | 
					        btnPrevPage.setAlpha(btnPrevPage.isEnabled() ? enabledAlpha : disabledAlpha);
 | 
				
			||||||
 | 
					        btnNextPage.setAlpha(btnNextPage.isEnabled() ? enabledAlpha : disabledAlpha);
 | 
				
			||||||
 | 
					        btnLastPage.setAlpha(btnLastPage.isEnabled() ? enabledAlpha : disabledAlpha);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ✅ NEW METHOD: Create page number buttons
 | 
				
			||||||
 | 
					    private void createPageNumbers() {
 | 
				
			||||||
 | 
					        pageNumbersContainer.removeAllViews();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Calculate which page numbers to show (max 5 numbers)
 | 
				
			||||||
 | 
					        int maxPageButtons = 5;
 | 
				
			||||||
 | 
					        int startPage = Math.max(1, currentPage - 2);
 | 
				
			||||||
 | 
					        int endPage = Math.min(totalPages, startPage + maxPageButtons - 1);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Adjust start if we're near the end
 | 
				
			||||||
 | 
					        if (endPage - startPage < maxPageButtons - 1) {
 | 
				
			||||||
 | 
					            startPage = Math.max(1, endPage - maxPageButtons + 1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // ✅ CONSISTENT BUTTON SIZE for all devices (more modern size)
 | 
				
			||||||
 | 
					        int buttonSize = (int) (44 * getResources().getDisplayMetrics().density); // 44dp (iOS standard)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        for (int i = startPage; i <= endPage; i++) {
 | 
				
			||||||
 | 
					            final int pageNumber = i;
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            TextView pageButton = new TextView(this);
 | 
				
			||||||
 | 
					            pageButton.setText(String.valueOf(pageNumber));
 | 
				
			||||||
 | 
					            pageButton.setTextSize(16); // Slightly larger text
 | 
				
			||||||
 | 
					            pageButton.setClickable(true);
 | 
				
			||||||
 | 
					            pageButton.setFocusable(true);
 | 
				
			||||||
 | 
					            pageButton.setGravity(android.view.Gravity.CENTER);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // ✅ USE STYLE HELPER
 | 
				
			||||||
 | 
					            boolean isActive = (pageNumber == currentPage);
 | 
				
			||||||
 | 
					            StyleHelper.applyPaginationButtonStyle(pageButton, this, isActive);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            pageButton.setOnClickListener(v -> goToPage(pageNumber));
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // ✅ IMPROVED: Better spacing like in the image
 | 
				
			||||||
 | 
					            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
 | 
				
			||||||
 | 
					                buttonSize, // Consistent width
 | 
				
			||||||
 | 
					                buttonSize  // Consistent height
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            params.setMargins(6, 0, 6, 0); // 6dp margin for better spacing
 | 
				
			||||||
 | 
					            pageButton.setLayoutParams(params);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            pageNumbersContainer.addView(pageButton);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Log.d("TransactionActivity", "🔢 Page buttons created: " + startPage + " to " + endPage + 
 | 
				
			||||||
 | 
					            " with size: " + buttonSize + "px");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void loadTransactions(int pageToLoad) {
 | 
					    private void loadTransactions(int pageToLoad) {
 | 
				
			||||||
        isLoading = true;
 | 
					        isLoading = true;
 | 
				
			||||||
        if (pageToLoad == 0) {
 | 
					        progressBar.setVisibility(View.VISIBLE);
 | 
				
			||||||
            progressBar.setVisibility(View.VISIBLE);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        new FetchTransactionsTask(pageToLoad).execute();
 | 
					        new FetchTransactionsTask(pageToLoad).execute();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private class FetchTransactionsTask extends AsyncTask<Void, Void, List<Transaction>> {
 | 
					    private class FetchTransactionsTask extends AsyncTask<Void, Void, List<Transaction>> {
 | 
				
			||||||
        private int pageToLoad;
 | 
					        private int pageToLoad;
 | 
				
			||||||
        private boolean error = false;
 | 
					        private boolean error = false;
 | 
				
			||||||
        private int total = 0;
 | 
					        private int apiTotal = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        FetchTransactionsTask(int page) {
 | 
					        FetchTransactionsTask(int page) {
 | 
				
			||||||
            this.pageToLoad = page;
 | 
					            this.pageToLoad = page;
 | 
				
			||||||
@ -226,13 +439,14 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
        protected List<Transaction> doInBackground(Void... voids) {
 | 
					        protected List<Transaction> doInBackground(Void... voids) {
 | 
				
			||||||
            List<Transaction> result = new ArrayList<>();
 | 
					            List<Transaction> result = new ArrayList<>();
 | 
				
			||||||
            try {
 | 
					            try {
 | 
				
			||||||
                // ✅ FETCH MORE DATA: Increased limit for better deduplication
 | 
					                // ✅ PAGINATION API CALL: Use page-based API call
 | 
				
			||||||
                int fetchLimit = limit * 3; // Get more records to handle all duplicates
 | 
					                int apiPage = pageToLoad - 1; // API uses 0-based indexing
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                String urlString = "https://be-edc.msvc.app/transactions?page=" + pageToLoad + 
 | 
					                String urlString = "https://be-edc.msvc.app/transactions?page=" + apiPage + 
 | 
				
			||||||
                    "&limit=" + fetchLimit + "&sortOrder=DESC&from_date=&to_date=&location_id=0&merchant_id=0&tid=73001500&mid=71000026521&sortColumn=created_at";
 | 
					                    "&limit=" + itemsPerPage + "&sortOrder=DESC&from_date=&to_date=&location_id=0&merchant_id=0&tid=73001500&mid=71000026521&sortColumn=created_at";
 | 
				
			||||||
                    
 | 
					                    
 | 
				
			||||||
                Log.d("TransactionActivity", "🔍 Fetching transactions page " + pageToLoad + " with limit " + fetchLimit);
 | 
					                Log.d("TransactionActivity", "🔍 Fetching transactions page " + pageToLoad + 
 | 
				
			||||||
 | 
					                    " (API page " + apiPage + ") with limit " + itemsPerPage + " - SORT: DESC by created_at");
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                URI uri = new URI(urlString);
 | 
					                URI uri = new URI(urlString);
 | 
				
			||||||
                URL url = uri.toURL();
 | 
					                URL url = uri.toURL();
 | 
				
			||||||
@ -256,10 +470,11 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
                    
 | 
					                    
 | 
				
			||||||
                    JSONObject jsonObject = new JSONObject(response.toString());
 | 
					                    JSONObject jsonObject = new JSONObject(response.toString());
 | 
				
			||||||
                    JSONObject results = jsonObject.getJSONObject("results");
 | 
					                    JSONObject results = jsonObject.getJSONObject("results");
 | 
				
			||||||
                    total = results.getInt("total");
 | 
					                    apiTotal = results.getInt("total");
 | 
				
			||||||
                    JSONArray data = results.getJSONArray("data");
 | 
					                    JSONArray data = results.getJSONArray("data");
 | 
				
			||||||
                    
 | 
					                    
 | 
				
			||||||
                    Log.d("TransactionActivity", "📊 Raw API response: " + data.length() + " records");
 | 
					                    Log.d("TransactionActivity", "📊 API response: " + data.length() + 
 | 
				
			||||||
 | 
					                        " records, total: " + apiTotal);
 | 
				
			||||||
                    
 | 
					                    
 | 
				
			||||||
                    // ✅ STEP 1: Parse all transactions from API
 | 
					                    // ✅ STEP 1: Parse all transactions from API
 | 
				
			||||||
                    List<Transaction> rawTransactions = new ArrayList<>();
 | 
					                    List<Transaction> rawTransactions = new ArrayList<>();
 | 
				
			||||||
@ -304,64 +519,109 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
            
 | 
					            
 | 
				
			||||||
            if (error) {
 | 
					            if (error) {
 | 
				
			||||||
                Toast.makeText(TransactionActivity.this, "Failed to fetch transactions", Toast.LENGTH_SHORT).show();
 | 
					                Toast.makeText(TransactionActivity.this, "Failed to fetch transactions", Toast.LENGTH_SHORT).show();
 | 
				
			||||||
 | 
					                updatePaginationDisplay(); // Show current state even on error
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            if (pageToLoad == 0) {
 | 
					            // ✅ UPDATE PAGINATION DATA
 | 
				
			||||||
                transactionList.clear();
 | 
					            currentPage = pageToLoad;
 | 
				
			||||||
                transactionCache.clear(); // Clear cache on refresh
 | 
					            totalRecords = apiTotal;
 | 
				
			||||||
            }
 | 
					            totalPages = (int) Math.ceil((double) totalRecords / itemsPerPage);
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // ✅ SMART MERGE: Only add truly new transactions
 | 
					            // ✅ UPDATE TRANSACTION LIST
 | 
				
			||||||
            int addedCount = 0;
 | 
					            transactionList.clear();
 | 
				
			||||||
            for (Transaction newTx : transactions) {
 | 
					            transactionList.addAll(transactions);
 | 
				
			||||||
                String refId = newTx.referenceId;
 | 
					 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
                // Check if we already have a better version of this transaction
 | 
					            // ✅ CRITICAL: FORCE SORT AGAIN after adding to main list
 | 
				
			||||||
                Transaction cachedTx = transactionCache.get(refId);
 | 
					            transactionList.sort((t1, t2) -> {
 | 
				
			||||||
                if (cachedTx == null || isBetterTransaction(newTx, cachedTx)) {
 | 
					                try {
 | 
				
			||||||
                    // Update cache with better transaction
 | 
					                    Date date1 = parseCreatedAtDate(t1.createdAt);
 | 
				
			||||||
                    transactionCache.put(refId, newTx);
 | 
					                    Date date2 = parseCreatedAtDate(t2.createdAt);
 | 
				
			||||||
                    
 | 
					                    
 | 
				
			||||||
                    // Update or add to main list
 | 
					                    if (date1 != null && date2 != null) {
 | 
				
			||||||
                    boolean updated = false;
 | 
					                        int comparison = date2.compareTo(date1); // Newest first
 | 
				
			||||||
                    for (int i = 0; i < transactionList.size(); i++) {
 | 
					                        Log.d("TransactionActivity", "🔄 Final sort: " + t1.createdAt + " vs " + t2.createdAt + " = " + comparison);
 | 
				
			||||||
                        if (transactionList.get(i).referenceId.equals(refId)) {
 | 
					                        return comparison;
 | 
				
			||||||
                            transactionList.set(i, newTx);
 | 
					 | 
				
			||||||
                            updated = true;
 | 
					 | 
				
			||||||
                            break;
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    
 | 
					 | 
				
			||||||
                    if (!updated) {
 | 
					 | 
				
			||||||
                        transactionList.add(newTx);
 | 
					 | 
				
			||||||
                        addedCount++;
 | 
					 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					                } catch (Exception e) {
 | 
				
			||||||
 | 
					                    Log.w("TransactionActivity", "Date comparison error: " + e.getMessage());
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					                return Integer.compare(t2.id, t1.id); // Fallback by ID
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            Log.d("TransactionActivity", "📋 Added " + addedCount + " new unique transactions. Total: " + transactionList.size());
 | 
					            Log.d("TransactionActivity", "📋 Page " + currentPage + " loaded and sorted: " + 
 | 
				
			||||||
 | 
					                transactions.size() + " transactions. Total: " + totalRecords + "/" + totalPages + " pages");
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // ✅ LOG FINAL ORDER VERIFICATION
 | 
				
			||||||
 | 
					            Log.d("TransactionActivity", "📋 FINAL DISPLAY ORDER:");
 | 
				
			||||||
 | 
					            for (int i = 0; i < Math.min(10, transactionList.size()); i++) {
 | 
				
			||||||
 | 
					                Transaction tx = transactionList.get(i);
 | 
				
			||||||
 | 
					                Log.d("TransactionActivity", "   " + (i+1) + ". " + tx.createdAt + " - " + tx.referenceId);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // Update filtered list based on current search
 | 
					            // Update filtered list based on current search
 | 
				
			||||||
            filterTransactions(currentSearchQuery);
 | 
					            filterTransactions(currentSearchQuery);
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            page = pageToLoad;
 | 
					            // Scroll to top
 | 
				
			||||||
            if (transactions.size() < limit) { // No more pages if returned less than requested
 | 
					            if (!filteredList.isEmpty()) {
 | 
				
			||||||
                isLastPage = true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // Scroll to top if it's a refresh
 | 
					 | 
				
			||||||
            if (pageToLoad == 0 && !filteredList.isEmpty()) {
 | 
					 | 
				
			||||||
                recyclerView.scrollToPosition(0);
 | 
					                recyclerView.scrollToPosition(0);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * ✅ ENHANCED DATE PARSING: Handle multiple date formats from API
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Date parseCreatedAtDate(String rawDate) {
 | 
				
			||||||
 | 
					        if (rawDate == null || rawDate.isEmpty()) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // List of possible date formats from API
 | 
				
			||||||
 | 
					        String[] possibleFormats = {
 | 
				
			||||||
 | 
					            "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",  // ISO format with milliseconds
 | 
				
			||||||
 | 
					            "yyyy-MM-dd'T'HH:mm:ss'Z'",      // ISO format without milliseconds
 | 
				
			||||||
 | 
					            "yyyy-MM-dd HH:mm:ss.SSS",       // Standard format with milliseconds
 | 
				
			||||||
 | 
					            "yyyy-MM-dd HH:mm:ss",           // Standard format
 | 
				
			||||||
 | 
					            "yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'" // ISO format with microseconds
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        for (String format : possibleFormats) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.getDefault());
 | 
				
			||||||
 | 
					                return sdf.parse(rawDate);
 | 
				
			||||||
 | 
					            } catch (Exception e) {
 | 
				
			||||||
 | 
					                // Continue to next format
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Manual parsing fallback for complex formats
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            String cleanedDate = rawDate.replace("T", " ").replace("Z", "");
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Remove microseconds/milliseconds if present
 | 
				
			||||||
 | 
					            if (cleanedDate.contains(".")) {
 | 
				
			||||||
 | 
					                cleanedDate = cleanedDate.substring(0, cleanedDate.indexOf("."));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
 | 
				
			||||||
 | 
					            return sdf.parse(cleanedDate);
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            Log.w("TransactionActivity", "❌ Could not parse date: " + rawDate);
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * ✅ ADVANCED DEDUPLICATION: Enhanced algorithm with multiple strategies
 | 
					     * ✅ ADVANCED DEDUPLICATION: Enhanced algorithm with multiple strategies
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private List<Transaction> applyAdvancedDeduplication(List<Transaction> rawTransactions) {
 | 
					    private List<Transaction> applyAdvancedDeduplication(List<Transaction> rawTransactions) {
 | 
				
			||||||
        Log.d("TransactionActivity", "🧠 Starting advanced deduplication...");
 | 
					        Log.d("TransactionActivity", "🧠 Starting advanced deduplication...");
 | 
				
			||||||
 | 
					        Log.d("TransactionActivity", "📥 Input transactions order (first 5):");
 | 
				
			||||||
 | 
					        for (int i = 0; i < Math.min(5, rawTransactions.size()); i++) {
 | 
				
			||||||
 | 
					            Transaction tx = rawTransactions.get(i);
 | 
				
			||||||
 | 
					            Log.d("TransactionActivity", "   " + (i+1) + ". ID:" + tx.id + " Date:" + tx.createdAt + " Ref:" + tx.referenceId);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // Strategy 1: Group by reference_id
 | 
					        // Strategy 1: Group by reference_id
 | 
				
			||||||
        Map<String, List<Transaction>> groupedByRef = new HashMap<>();
 | 
					        Map<String, List<Transaction>> groupedByRef = new HashMap<>();
 | 
				
			||||||
@ -384,13 +644,26 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
                deduplicatedList.add(group.get(0));
 | 
					                deduplicatedList.add(group.get(0));
 | 
				
			||||||
                Log.d("TransactionActivity", "✅ Unique transaction: " + referenceId);
 | 
					                Log.d("TransactionActivity", "✅ Unique transaction: " + referenceId);
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                // Multiple transactions with same reference_id
 | 
					                // Multiple transactions with same reference_id - sort group by date first
 | 
				
			||||||
 | 
					                group.sort((t1, t2) -> {
 | 
				
			||||||
 | 
					                    try {
 | 
				
			||||||
 | 
					                        Date date1 = parseCreatedAtDate(t1.createdAt);
 | 
				
			||||||
 | 
					                        Date date2 = parseCreatedAtDate(t2.createdAt);
 | 
				
			||||||
 | 
					                        if (date1 != null && date2 != null) {
 | 
				
			||||||
 | 
					                            return date2.compareTo(date1); // Newest first in group
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    } catch (Exception e) {
 | 
				
			||||||
 | 
					                        // Fallback to ID
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    return Integer.compare(t2.id, t1.id);
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
                Transaction bestTransaction = selectBestTransactionAdvanced(group, referenceId);
 | 
					                Transaction bestTransaction = selectBestTransactionAdvanced(group, referenceId);
 | 
				
			||||||
                deduplicatedList.add(bestTransaction);
 | 
					                deduplicatedList.add(bestTransaction);
 | 
				
			||||||
                duplicatesRemoved += (group.size() - 1);
 | 
					                duplicatesRemoved += (group.size() - 1);
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                Log.d("TransactionActivity", "🔄 Deduplicated " + group.size() + " → 1 for ref: " + referenceId + 
 | 
					                Log.d("TransactionActivity", "🔄 Deduplicated " + group.size() + " → 1 for ref: " + referenceId + 
 | 
				
			||||||
                    " (kept ID: " + bestTransaction.id + ", status: " + bestTransaction.status + ")");
 | 
					                    " (kept ID: " + bestTransaction.id + ", status: " + bestTransaction.status + ", date: " + bestTransaction.createdAt + ")");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
@ -410,10 +683,12 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
        
 | 
					        
 | 
				
			||||||
        Transaction bestTransaction = duplicates.get(0);
 | 
					        Transaction bestTransaction = duplicates.get(0);
 | 
				
			||||||
        int bestPriority = getStatusPriority(bestTransaction.status);
 | 
					        int bestPriority = getStatusPriority(bestTransaction.status);
 | 
				
			||||||
 | 
					        Date bestDate = parseCreatedAtDate(bestTransaction.createdAt);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // Detailed analysis of each candidate
 | 
					        // Detailed analysis of each candidate
 | 
				
			||||||
        for (Transaction tx : duplicates) {
 | 
					        for (Transaction tx : duplicates) {
 | 
				
			||||||
            int currentPriority = getStatusPriority(tx.status);
 | 
					            int currentPriority = getStatusPriority(tx.status);
 | 
				
			||||||
 | 
					            Date currentDate = parseCreatedAtDate(tx.createdAt);
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            Log.d("TransactionActivity", "   📊 Candidate: ID=" + tx.id + 
 | 
					            Log.d("TransactionActivity", "   📊 Candidate: ID=" + tx.id + 
 | 
				
			||||||
                ", Status=" + tx.status + " (priority=" + currentPriority + ")" +
 | 
					                ", Status=" + tx.status + " (priority=" + currentPriority + ")" +
 | 
				
			||||||
@ -429,20 +704,24 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            // Rule 2: Same priority, choose newer timestamp
 | 
					            // Rule 2: Same priority, choose newer timestamp
 | 
				
			||||||
            else if (currentPriority == bestPriority) {
 | 
					            else if (currentPriority == bestPriority) {
 | 
				
			||||||
                if (isNewerTransaction(tx, bestTransaction)) {
 | 
					                if (currentDate != null && bestDate != null && currentDate.after(bestDate)) {
 | 
				
			||||||
                    shouldSelect = true;
 | 
					                    shouldSelect = true;
 | 
				
			||||||
                    reason = "newer timestamp";
 | 
					                    reason = "newer timestamp";
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                // Rule 3: Same priority and time, choose higher ID
 | 
					                // Rule 3: Same priority and time, choose higher ID
 | 
				
			||||||
                else if (tx.createdAt.equals(bestTransaction.createdAt) && tx.id > bestTransaction.id) {
 | 
					                else if ((currentDate == null && bestDate == null) || 
 | 
				
			||||||
                    shouldSelect = true;
 | 
					                         (currentDate != null && bestDate != null && currentDate.equals(bestDate))) {
 | 
				
			||||||
                    reason = "higher ID";
 | 
					                    if (tx.id > bestTransaction.id) {
 | 
				
			||||||
 | 
					                        shouldSelect = true;
 | 
				
			||||||
 | 
					                        reason = "higher ID";
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            if (shouldSelect) {
 | 
					            if (shouldSelect) {
 | 
				
			||||||
                bestTransaction = tx;
 | 
					                bestTransaction = tx;
 | 
				
			||||||
                bestPriority = currentPriority;
 | 
					                bestPriority = currentPriority;
 | 
				
			||||||
 | 
					                bestDate = currentDate;
 | 
				
			||||||
                Log.d("TransactionActivity", "   ⭐ NEW BEST selected: " + reason);
 | 
					                Log.d("TransactionActivity", "   ⭐ NEW BEST selected: " + reason);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -454,19 +733,18 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * ✅ TIMESTAMP COMPARISON: Smart date comparison
 | 
					     * ✅ TIMESTAMP COMPARISON: Smart date comparison using enhanced parsing
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private boolean isNewerTransaction(Transaction tx1, Transaction tx2) {
 | 
					    private boolean isNewerTransaction(Transaction tx1, Transaction tx2) {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
 | 
					            Date date1 = parseCreatedAtDate(tx1.createdAt);
 | 
				
			||||||
            Date date1 = sdf.parse(tx1.createdAt);
 | 
					            Date date2 = parseCreatedAtDate(tx2.createdAt);
 | 
				
			||||||
            Date date2 = sdf.parse(tx2.createdAt);
 | 
					 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            if (date1 != null && date2 != null) {
 | 
					            if (date1 != null && date2 != null) {
 | 
				
			||||||
                return date1.after(date2);
 | 
					                return date1.after(date2);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } catch (Exception e) {
 | 
					        } catch (Exception e) {
 | 
				
			||||||
            Log.w("TransactionActivity", "Date parsing error, falling back to ID comparison");
 | 
					            Log.w("TransactionActivity", "Date comparison error, falling back to ID comparison");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // Fallback: higher ID usually means newer
 | 
					        // Fallback: higher ID usually means newer
 | 
				
			||||||
@ -474,7 +752,7 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * ✅ COMPARISON HELPER: Check if one transaction is better than another
 | 
					     * ✅ COMPARISON HELPER: Check if one transaction is better than another using enhanced parsing
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private boolean isBetterTransaction(Transaction newTx, Transaction existingTx) {
 | 
					    private boolean isBetterTransaction(Transaction newTx, Transaction existingTx) {
 | 
				
			||||||
        int newPriority = getStatusPriority(newTx.status);
 | 
					        int newPriority = getStatusPriority(newTx.status);
 | 
				
			||||||
 | 
				
			|||||||
@ -11,18 +11,16 @@ import androidx.annotation.NonNull;
 | 
				
			|||||||
import androidx.recyclerview.widget.RecyclerView;
 | 
					import androidx.recyclerview.widget.RecyclerView;
 | 
				
			||||||
import androidx.core.content.ContextCompat;
 | 
					import androidx.core.content.ContextCompat;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ✅ TAMBAHKAN MISSING IMPORTS INI:
 | 
					 | 
				
			||||||
import java.io.BufferedReader;
 | 
					import java.io.BufferedReader;
 | 
				
			||||||
import java.io.IOException;
 | 
					import java.io.IOException;
 | 
				
			||||||
import java.io.InputStreamReader;
 | 
					import java.io.InputStreamReader;
 | 
				
			||||||
import java.net.HttpURLConnection;
 | 
					import java.net.HttpURLConnection;
 | 
				
			||||||
import java.net.URL;
 | 
					import java.net.URL;
 | 
				
			||||||
import java.net.URLEncoder;
 | 
					 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
import java.text.NumberFormat;
 | 
					 | 
				
			||||||
import java.util.Locale;
 | 
					import java.util.Locale;
 | 
				
			||||||
 | 
					import java.text.SimpleDateFormat;
 | 
				
			||||||
 | 
					import java.util.Date;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ✅ TAMBAHKAN JSON IMPORTS:
 | 
					 | 
				
			||||||
import org.json.JSONArray;
 | 
					import org.json.JSONArray;
 | 
				
			||||||
import org.json.JSONException;
 | 
					import org.json.JSONException;
 | 
				
			||||||
import org.json.JSONObject;
 | 
					import org.json.JSONObject;
 | 
				
			||||||
@ -43,6 +41,16 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
        this.printClickListener = listener;
 | 
					        this.printClickListener = listener;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Update data without numbering (removed as per request)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void updateData(List<TransactionActivity.Transaction> newData, int startIndex) {
 | 
				
			||||||
 | 
					        this.transactionList = newData;
 | 
				
			||||||
 | 
					        notifyDataSetChanged();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Log.d("TransactionAdapter", "📋 Data updated: " + newData.size() + " items");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @NonNull
 | 
					    @NonNull
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public TransactionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
 | 
					    public TransactionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
 | 
				
			||||||
@ -54,6 +62,16 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
    public void onBindViewHolder(@NonNull TransactionViewHolder holder, int position) {
 | 
					    public void onBindViewHolder(@NonNull TransactionViewHolder holder, int position) {
 | 
				
			||||||
        TransactionActivity.Transaction t = transactionList.get(position);
 | 
					        TransactionActivity.Transaction t = transactionList.get(position);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
 | 
					        // ✅ STRIPE TABLE: Set alternating row colors
 | 
				
			||||||
 | 
					        LinearLayout itemContainer = holder.itemView.findViewById(R.id.itemContainer);
 | 
				
			||||||
 | 
					        if (position % 2 == 0) {
 | 
				
			||||||
 | 
					            // Even rows - white background
 | 
				
			||||||
 | 
					            itemContainer.setBackgroundColor(ContextCompat.getColor(holder.itemView.getContext(), android.R.color.white));
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // Odd rows - light gray background  
 | 
				
			||||||
 | 
					            itemContainer.setBackgroundColor(ContextCompat.getColor(holder.itemView.getContext(), android.R.color.background_light));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        Log.d("TransactionAdapter", "📋 Binding transaction " + position + ":");
 | 
					        Log.d("TransactionAdapter", "📋 Binding transaction " + position + ":");
 | 
				
			||||||
        Log.d("TransactionAdapter", "   Reference: " + t.referenceId);
 | 
					        Log.d("TransactionAdapter", "   Reference: " + t.referenceId);
 | 
				
			||||||
        Log.d("TransactionAdapter", "   Status: " + t.status);
 | 
					        Log.d("TransactionAdapter", "   Status: " + t.status);
 | 
				
			||||||
@ -87,8 +105,7 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
            if (t.referenceId != null && !t.referenceId.isEmpty()) {
 | 
					            if (t.referenceId != null && !t.referenceId.isEmpty()) {
 | 
				
			||||||
                // Show checking state
 | 
					                // Show checking state
 | 
				
			||||||
                holder.status.setText("CHECKING...");
 | 
					                holder.status.setText("CHECKING...");
 | 
				
			||||||
                holder.status.setTextColor(ContextCompat.getColor(holder.itemView.getContext(), 
 | 
					                StyleHelper.applyStatusTextColor(holder.status, holder.itemView.getContext(), "CHECKING");
 | 
				
			||||||
                    android.R.color.holo_orange_dark));
 | 
					 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                Log.d("TransactionAdapter", "🔄 Starting comprehensive check for: " + t.referenceId);
 | 
					                Log.d("TransactionAdapter", "🔄 Starting comprehensive check for: " + t.referenceId);
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
@ -97,13 +114,13 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                // No reference ID to check
 | 
					                // No reference ID to check
 | 
				
			||||||
                holder.status.setText(displayStatus.toUpperCase());
 | 
					                holder.status.setText(displayStatus.toUpperCase());
 | 
				
			||||||
                setStatusColor(holder.status, displayStatus);
 | 
					                StyleHelper.applyStatusTextColor(holder.status, holder.itemView.getContext(), displayStatus);
 | 
				
			||||||
                Log.w("TransactionAdapter", "⚠️ No reference ID for status check");
 | 
					                Log.w("TransactionAdapter", "⚠️ No reference ID for status check");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            // Use existing status yang sudah confirmed
 | 
					            // Use existing status yang sudah confirmed
 | 
				
			||||||
            holder.status.setText(displayStatus.toUpperCase());
 | 
					            holder.status.setText(displayStatus.toUpperCase());
 | 
				
			||||||
            setStatusColor(holder.status, displayStatus);
 | 
					            StyleHelper.applyStatusTextColor(holder.status, holder.itemView.getContext(), displayStatus);
 | 
				
			||||||
            Log.d("TransactionAdapter", "✅ Using confirmed status: " + displayStatus);
 | 
					            Log.d("TransactionAdapter", "✅ Using confirmed status: " + displayStatus);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -111,6 +128,12 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
        String paymentMethod = getPaymentMethodName(t.channelCode, t.channelCategory);
 | 
					        String paymentMethod = getPaymentMethodName(t.channelCode, t.channelCategory);
 | 
				
			||||||
        holder.paymentMethod.setText(paymentMethod);
 | 
					        holder.paymentMethod.setText(paymentMethod);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // ✅ FORMAT AND DISPLAY CREATED AT
 | 
				
			||||||
 | 
					        String formattedDate = formatCreatedAtDate(t.createdAt);
 | 
				
			||||||
 | 
					        holder.createdAt.setText(formattedDate);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Log.d("TransactionAdapter", "📅 Created at: " + t.createdAt + " -> " + formattedDate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Set click listeners
 | 
					        // Set click listeners
 | 
				
			||||||
        holder.itemView.setOnClickListener(v -> {
 | 
					        holder.itemView.setOnClickListener(v -> {
 | 
				
			||||||
            if (printClickListener != null) {
 | 
					            if (printClickListener != null) {
 | 
				
			||||||
@ -188,35 +211,6 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
        return "Rp. " + formatted;
 | 
					        return "Rp. " + formatted;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void setStatusColor(TextView statusTextView, String status) {
 | 
					 | 
				
			||||||
        String statusLower = status.toLowerCase();
 | 
					 | 
				
			||||||
        int color;
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        if (statusLower.equals("failed") || statusLower.equals("failure") || 
 | 
					 | 
				
			||||||
            statusLower.equals("error") || statusLower.equals("declined") || 
 | 
					 | 
				
			||||||
            statusLower.equals("expire") || statusLower.equals("cancel")) {
 | 
					 | 
				
			||||||
            // Red for failed/error statuses
 | 
					 | 
				
			||||||
            color = ContextCompat.getColor(statusTextView.getContext(), android.R.color.holo_red_dark);
 | 
					 | 
				
			||||||
        } else if (statusLower.equals("success") || statusLower.equals("paid") || 
 | 
					 | 
				
			||||||
                statusLower.equals("settlement") || statusLower.equals("completed") ||
 | 
					 | 
				
			||||||
                statusLower.equals("capture")) {
 | 
					 | 
				
			||||||
            // Green for successful statuses
 | 
					 | 
				
			||||||
            color = ContextCompat.getColor(statusTextView.getContext(), android.R.color.holo_green_dark);
 | 
					 | 
				
			||||||
        } else if (statusLower.equals("pending") || statusLower.equals("processing") || 
 | 
					 | 
				
			||||||
                statusLower.equals("waiting") || statusLower.equals("checking...")) {
 | 
					 | 
				
			||||||
            // Orange for pending/processing statuses
 | 
					 | 
				
			||||||
            color = ContextCompat.getColor(statusTextView.getContext(), android.R.color.holo_orange_dark);
 | 
					 | 
				
			||||||
        } else if (statusLower.equals("init")) {
 | 
					 | 
				
			||||||
            // Yellow for init status
 | 
					 | 
				
			||||||
            color = ContextCompat.getColor(statusTextView.getContext(), android.R.color.holo_orange_light);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            // Default gray for unknown statuses
 | 
					 | 
				
			||||||
            color = ContextCompat.getColor(statusTextView.getContext(), android.R.color.darker_gray);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        statusTextView.setTextColor(color);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void checkMidtransStatus(String referenceId, TextView statusTextView) {
 | 
					    private void checkMidtransStatus(String referenceId, TextView statusTextView) {
 | 
				
			||||||
        new Thread(() -> {
 | 
					        new Thread(() -> {
 | 
				
			||||||
            try {
 | 
					            try {
 | 
				
			||||||
@ -344,7 +338,7 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
                    
 | 
					                    
 | 
				
			||||||
                    statusTextView.post(() -> {
 | 
					                    statusTextView.post(() -> {
 | 
				
			||||||
                        statusTextView.setText(finalStatus);
 | 
					                        statusTextView.setText(finalStatus);
 | 
				
			||||||
                        setStatusColor(statusTextView, finalStatus);
 | 
					                        StyleHelper.applyStatusTextColor(statusTextView, statusTextView.getContext(), finalStatus);
 | 
				
			||||||
                        
 | 
					                        
 | 
				
			||||||
                        Log.d("TransactionAdapter", "🎨 UI UPDATED:");
 | 
					                        Log.d("TransactionAdapter", "🎨 UI UPDATED:");
 | 
				
			||||||
                        Log.d("TransactionAdapter", "   Reference: " + referenceId);
 | 
					                        Log.d("TransactionAdapter", "   Reference: " + referenceId);
 | 
				
			||||||
@ -356,7 +350,7 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
                    Log.w("TransactionAdapter", "⚠️ API call failed with code: " + conn.getResponseCode());
 | 
					                    Log.w("TransactionAdapter", "⚠️ API call failed with code: " + conn.getResponseCode());
 | 
				
			||||||
                    statusTextView.post(() -> {
 | 
					                    statusTextView.post(() -> {
 | 
				
			||||||
                        statusTextView.setText("ERROR");
 | 
					                        statusTextView.setText("ERROR");
 | 
				
			||||||
                        setStatusColor(statusTextView, "ERROR");
 | 
					                        StyleHelper.applyStatusTextColor(statusTextView, statusTextView.getContext(), "ERROR");
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
@ -364,12 +358,108 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
                Log.e("TransactionAdapter", "❌ Comprehensive status check error: " + e.getMessage(), e);
 | 
					                Log.e("TransactionAdapter", "❌ Comprehensive status check error: " + e.getMessage(), e);
 | 
				
			||||||
                statusTextView.post(() -> {
 | 
					                statusTextView.post(() -> {
 | 
				
			||||||
                    statusTextView.setText("INIT");
 | 
					                    statusTextView.setText("INIT");
 | 
				
			||||||
                    setStatusColor(statusTextView, "INIT");
 | 
					                    StyleHelper.applyStatusTextColor(statusTextView, statusTextView.getContext(), "INIT");
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }).start();
 | 
					        }).start();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Format created_at date to readable format
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String formatCreatedAtDate(String rawDate) {
 | 
				
			||||||
 | 
					        if (rawDate == null || rawDate.isEmpty()) {
 | 
				
			||||||
 | 
					            return "N/A";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Log.d("TransactionAdapter", "📅 Input date: '" + rawDate + "'");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            // Handle different possible input formats from API
 | 
				
			||||||
 | 
					            SimpleDateFormat inputFormat;
 | 
				
			||||||
 | 
					            String cleanedDate = rawDate;
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if (rawDate.contains("T")) {
 | 
				
			||||||
 | 
					                // ISO format: "2025-06-10T04:31:19.565Z"
 | 
				
			||||||
 | 
					                cleanedDate = rawDate.replace("T", " ").replace("Z", "");
 | 
				
			||||||
 | 
					                // Remove microseconds if present
 | 
				
			||||||
 | 
					                if (cleanedDate.contains(".")) {
 | 
				
			||||||
 | 
					                    cleanedDate = cleanedDate.substring(0, cleanedDate.indexOf("."));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                inputFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
 | 
				
			||||||
 | 
					            } else if (rawDate.length() > 19 && rawDate.contains(".")) {
 | 
				
			||||||
 | 
					                // Format with microseconds: "2025-06-10 04:31:19.565"
 | 
				
			||||||
 | 
					                cleanedDate = rawDate.substring(0, 19); // Cut off microseconds
 | 
				
			||||||
 | 
					                inputFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // Standard format: "2025-06-10 04:31:19"
 | 
				
			||||||
 | 
					                inputFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            Log.d("TransactionAdapter", "📅 Cleaned date: '" + cleanedDate + "'");
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Output format: d/M/yyyy H:mm:ss
 | 
				
			||||||
 | 
					            SimpleDateFormat outputFormat = new SimpleDateFormat("d/M/yyyy H:mm:ss", Locale.getDefault());
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            Date date = inputFormat.parse(cleanedDate);
 | 
				
			||||||
 | 
					            if (date != null) {
 | 
				
			||||||
 | 
					                String formatted = outputFormat.format(date);
 | 
				
			||||||
 | 
					                Log.d("TransactionAdapter", "📅 Date formatted: " + rawDate + " -> " + formatted);
 | 
				
			||||||
 | 
					                return formatted;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            Log.e("TransactionAdapter", "❌ Date formatting error for: " + rawDate, e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Fallback: Manual parsing
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            // Handle format like "2025-06-10T04:31:19.565Z" manually
 | 
				
			||||||
 | 
					            String workingDate = rawDate.replace("T", " ").replace("Z", "");
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Remove microseconds if present
 | 
				
			||||||
 | 
					            if (workingDate.contains(".")) {
 | 
				
			||||||
 | 
					                workingDate = workingDate.substring(0, workingDate.indexOf("."));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            Log.d("TransactionAdapter", "📅 Manual parsing attempt: '" + workingDate + "'");
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Split into date and time parts
 | 
				
			||||||
 | 
					            String[] parts = workingDate.split(" ");
 | 
				
			||||||
 | 
					            if (parts.length >= 2) {
 | 
				
			||||||
 | 
					                String datePart = parts[0]; // "2025-06-10"
 | 
				
			||||||
 | 
					                String timePart = parts[1]; // "04:31:19"
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                String[] dateComponents = datePart.split("-");
 | 
				
			||||||
 | 
					                if (dateComponents.length == 3) {
 | 
				
			||||||
 | 
					                    String year = dateComponents[0];
 | 
				
			||||||
 | 
					                    String month = dateComponents[1];
 | 
				
			||||||
 | 
					                    String day = dateComponents[2];
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    // Remove leading zeros and format as d/M/yyyy H:mm:ss
 | 
				
			||||||
 | 
					                    int dayInt = Integer.parseInt(day);
 | 
				
			||||||
 | 
					                    int monthInt = Integer.parseInt(month);
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    // Parse time to remove leading zeros from hour
 | 
				
			||||||
 | 
					                    String[] timeComponents = timePart.split(":");
 | 
				
			||||||
 | 
					                    if (timeComponents.length >= 3) {
 | 
				
			||||||
 | 
					                        int hour = Integer.parseInt(timeComponents[0]);
 | 
				
			||||||
 | 
					                        String minute = timeComponents[1];
 | 
				
			||||||
 | 
					                        String second = timeComponents[2];
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        String result = dayInt + "/" + monthInt + "/" + year + " " + hour + ":" + minute + ":" + second;
 | 
				
			||||||
 | 
					                        Log.d("TransactionAdapter", "📅 Manual format result: " + result);
 | 
				
			||||||
 | 
					                        return result;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            Log.w("TransactionAdapter", "❌ Manual date formatting failed: " + e.getMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Log.w("TransactionAdapter", "📅 Using fallback - returning original date: " + rawDate);
 | 
				
			||||||
 | 
					        return rawDate;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private String getPaymentMethodName(String channelCode, String channelCategory) {
 | 
					    private String getPaymentMethodName(String channelCode, String channelCategory) {
 | 
				
			||||||
        // Convert channel code to readable payment method name
 | 
					        // Convert channel code to readable payment method name
 | 
				
			||||||
        if (channelCode == null) return "Unknown";
 | 
					        if (channelCode == null) return "Unknown";
 | 
				
			||||||
@ -408,7 +498,7 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static class TransactionViewHolder extends RecyclerView.ViewHolder {
 | 
					    static class TransactionViewHolder extends RecyclerView.ViewHolder {
 | 
				
			||||||
        TextView amount, referenceId, status, paymentMethod;
 | 
					        TextView amount, referenceId, status, paymentMethod, createdAt; // ✅ Added createdAt
 | 
				
			||||||
        LinearLayout printSection;
 | 
					        LinearLayout printSection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public TransactionViewHolder(@NonNull View itemView) {
 | 
					        public TransactionViewHolder(@NonNull View itemView) {
 | 
				
			||||||
@ -417,6 +507,7 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
            referenceId = itemView.findViewById(R.id.textReferenceId);
 | 
					            referenceId = itemView.findViewById(R.id.textReferenceId);
 | 
				
			||||||
            status = itemView.findViewById(R.id.textStatus);
 | 
					            status = itemView.findViewById(R.id.textStatus);
 | 
				
			||||||
            paymentMethod = itemView.findViewById(R.id.textPaymentMethod);
 | 
					            paymentMethod = itemView.findViewById(R.id.textPaymentMethod);
 | 
				
			||||||
 | 
					            createdAt = itemView.findViewById(R.id.textCreatedAt); // ✅ Added createdAt
 | 
				
			||||||
            printSection = itemView.findViewById(R.id.printSection);
 | 
					            printSection = itemView.findViewById(R.id.printSection);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -23,39 +23,120 @@
 | 
				
			|||||||
        android:layout_width="match_parent"
 | 
					        android:layout_width="match_parent"
 | 
				
			||||||
        android:layout_height="match_parent"
 | 
					        android:layout_height="match_parent"
 | 
				
			||||||
        android:orientation="vertical"
 | 
					        android:orientation="vertical"
 | 
				
			||||||
        android:background="#f5f5f5"
 | 
					        android:background="#f8f9fa"
 | 
				
			||||||
        app:layout_behavior="@string/appbar_scrolling_view_behavior">
 | 
					        app:layout_behavior="@string/appbar_scrolling_view_behavior">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <!-- Search Section -->
 | 
					        <!-- Search and Filter Section -->
 | 
				
			||||||
        <LinearLayout
 | 
					        <LinearLayout
 | 
				
			||||||
            android:layout_width="match_parent"
 | 
					            android:layout_width="match_parent"
 | 
				
			||||||
            android:layout_height="wrap_content"
 | 
					            android:layout_height="wrap_content"
 | 
				
			||||||
            android:orientation="horizontal"
 | 
					            android:orientation="horizontal"
 | 
				
			||||||
            android:padding="16dp"
 | 
					            android:padding="20dp"
 | 
				
			||||||
            android:background="@android:color/white"
 | 
					            android:background="@android:color/white"
 | 
				
			||||||
            android:gravity="center_vertical">
 | 
					            android:gravity="center_vertical">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <EditText
 | 
					            <!-- Search Bar -->
 | 
				
			||||||
                android:id="@+id/searchEditText"
 | 
					            <LinearLayout
 | 
				
			||||||
 | 
					                android:id="@+id/searchContainer"
 | 
				
			||||||
                android:layout_width="0dp"
 | 
					                android:layout_width="0dp"
 | 
				
			||||||
                android:layout_height="48dp"
 | 
					                android:layout_height="56dp"
 | 
				
			||||||
                android:layout_weight="1"
 | 
					                android:layout_weight="1"
 | 
				
			||||||
                android:background="@drawable/search_background"
 | 
					                android:orientation="horizontal"
 | 
				
			||||||
                android:hint="Cari dengan nomor struk..."
 | 
					                android:gravity="center_vertical"
 | 
				
			||||||
                android:paddingStart="16dp"
 | 
					                android:paddingLeft="20dp"
 | 
				
			||||||
                android:paddingEnd="16dp"
 | 
					                android:paddingRight="20dp"
 | 
				
			||||||
                android:textSize="14sp"
 | 
					                android:layout_marginEnd="16dp">
 | 
				
			||||||
                android:textColorHint="#999999" />
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <ImageButton
 | 
					                <ImageView
 | 
				
			||||||
                android:id="@+id/searchButton"
 | 
					                    android:layout_width="24dp"
 | 
				
			||||||
                android:layout_width="48dp"
 | 
					                    android:layout_height="24dp"
 | 
				
			||||||
                android:layout_height="48dp"
 | 
					                    android:src="@android:drawable/ic_menu_search"
 | 
				
			||||||
                android:layout_marginStart="8dp"
 | 
					                    android:layout_marginEnd="12dp"
 | 
				
			||||||
                android:background="@drawable/search_button_background"
 | 
					                    android:alpha="0.5" />
 | 
				
			||||||
                android:src="@android:drawable/ic_menu_search"
 | 
					
 | 
				
			||||||
                android:contentDescription="Search"
 | 
					                <EditText
 | 
				
			||||||
                app:tint="@android:color/white" />
 | 
					                    android:id="@+id/searchEditText"
 | 
				
			||||||
 | 
					                    android:layout_width="0dp"
 | 
				
			||||||
 | 
					                    android:layout_height="match_parent"
 | 
				
			||||||
 | 
					                    android:layout_weight="1"
 | 
				
			||||||
 | 
					                    android:background="@android:color/transparent"
 | 
				
			||||||
 | 
					                    android:hint="Search"
 | 
				
			||||||
 | 
					                    android:textSize="16sp"
 | 
				
			||||||
 | 
					                    android:textColorHint="#999999"
 | 
				
			||||||
 | 
					                    android:textColor="#333333"
 | 
				
			||||||
 | 
					                    android:maxLines="1"
 | 
				
			||||||
 | 
					                    android:gravity="center_vertical" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- Filter Button -->
 | 
				
			||||||
 | 
					            <LinearLayout
 | 
				
			||||||
 | 
					                android:id="@+id/filterButton"
 | 
				
			||||||
 | 
					                android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                android:layout_height="56dp"
 | 
				
			||||||
 | 
					                android:orientation="horizontal"
 | 
				
			||||||
 | 
					                android:gravity="center"
 | 
				
			||||||
 | 
					                android:paddingLeft="20dp"
 | 
				
			||||||
 | 
					                android:paddingRight="20dp"
 | 
				
			||||||
 | 
					                android:clickable="true"
 | 
				
			||||||
 | 
					                android:focusable="true">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <ImageView
 | 
				
			||||||
 | 
					                    android:layout_width="20dp"
 | 
				
			||||||
 | 
					                    android:layout_height="20dp"
 | 
				
			||||||
 | 
					                    android:src="@android:drawable/ic_menu_sort_by_size"
 | 
				
			||||||
 | 
					                    android:layout_marginEnd="8dp"
 | 
				
			||||||
 | 
					                    android:alpha="0.5" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:text="Filter"
 | 
				
			||||||
 | 
					                    android:textSize="16sp"
 | 
				
			||||||
 | 
					                    android:textColor="#666666" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- Table Header -->
 | 
				
			||||||
 | 
					        <LinearLayout
 | 
				
			||||||
 | 
					            android:layout_width="match_parent"
 | 
				
			||||||
 | 
					            android:layout_height="48dp"
 | 
				
			||||||
 | 
					            android:orientation="horizontal"
 | 
				
			||||||
 | 
					            android:background="#f5f5f5"
 | 
				
			||||||
 | 
					            android:gravity="center_vertical"
 | 
				
			||||||
 | 
					            android:paddingLeft="16dp"
 | 
				
			||||||
 | 
					            android:paddingRight="16dp">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <TextView
 | 
				
			||||||
 | 
					                android:layout_width="0dp"
 | 
				
			||||||
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                android:layout_weight="2"
 | 
				
			||||||
 | 
					                android:text="Nomor Struk"
 | 
				
			||||||
 | 
					                android:textSize="14sp"
 | 
				
			||||||
 | 
					                android:textStyle="bold"
 | 
				
			||||||
 | 
					                android:textColor="#666666" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <TextView
 | 
				
			||||||
 | 
					                android:layout_width="0dp"
 | 
				
			||||||
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                android:layout_weight="1"
 | 
				
			||||||
 | 
					                android:text="Amount"
 | 
				
			||||||
 | 
					                android:textSize="14sp"
 | 
				
			||||||
 | 
					                android:textStyle="bold"
 | 
				
			||||||
 | 
					                android:textColor="#666666"
 | 
				
			||||||
 | 
					                android:gravity="center" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <TextView
 | 
				
			||||||
 | 
					                android:layout_width="0dp"
 | 
				
			||||||
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                android:layout_weight="1"
 | 
				
			||||||
 | 
					                android:text="Action"
 | 
				
			||||||
 | 
					                android:textSize="14sp"
 | 
				
			||||||
 | 
					                android:textStyle="bold"
 | 
				
			||||||
 | 
					                android:textColor="#666666"
 | 
				
			||||||
 | 
					                android:gravity="center" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        </LinearLayout>
 | 
					        </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -70,8 +151,73 @@
 | 
				
			|||||||
        <androidx.recyclerview.widget.RecyclerView
 | 
					        <androidx.recyclerview.widget.RecyclerView
 | 
				
			||||||
            android:id="@+id/recyclerView"
 | 
					            android:id="@+id/recyclerView"
 | 
				
			||||||
            android:layout_width="match_parent"
 | 
					            android:layout_width="match_parent"
 | 
				
			||||||
            android:layout_height="match_parent"
 | 
					            android:layout_height="0dp"
 | 
				
			||||||
            android:background="@android:color/white" />
 | 
					            android:layout_weight="1"
 | 
				
			||||||
 | 
					            android:background="#ffffff" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- Pagination Controls -->
 | 
				
			||||||
 | 
					        <LinearLayout
 | 
				
			||||||
 | 
					            android:id="@+id/paginationControls"
 | 
				
			||||||
 | 
					            android:layout_width="match_parent"
 | 
				
			||||||
 | 
					            android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					            android:orientation="horizontal"
 | 
				
			||||||
 | 
					            android:background="@android:color/white"
 | 
				
			||||||
 | 
					            android:padding="20dp"
 | 
				
			||||||
 | 
					            android:gravity="center"
 | 
				
			||||||
 | 
					            android:elevation="2dp"
 | 
				
			||||||
 | 
					            android:visibility="gone">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- First Page Button -->
 | 
				
			||||||
 | 
					            <ImageButton
 | 
				
			||||||
 | 
					                android:id="@+id/btnFirstPage"
 | 
				
			||||||
 | 
					                android:layout_width="44dp"
 | 
				
			||||||
 | 
					                android:layout_height="44dp"
 | 
				
			||||||
 | 
					                android:src="@android:drawable/ic_media_previous"
 | 
				
			||||||
 | 
					                android:contentDescription="First Page"
 | 
				
			||||||
 | 
					                android:layout_marginEnd="8dp"
 | 
				
			||||||
 | 
					                android:scaleType="centerInside" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- Previous Page Button -->
 | 
				
			||||||
 | 
					            <ImageButton
 | 
				
			||||||
 | 
					                android:id="@+id/btnPrevPage"
 | 
				
			||||||
 | 
					                android:layout_width="44dp"
 | 
				
			||||||
 | 
					                android:layout_height="44dp"
 | 
				
			||||||
 | 
					                android:src="@android:drawable/ic_media_rew"
 | 
				
			||||||
 | 
					                android:contentDescription="Previous Page"
 | 
				
			||||||
 | 
					                android:layout_marginEnd="12dp"
 | 
				
			||||||
 | 
					                android:scaleType="centerInside" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- Page Numbers Container -->
 | 
				
			||||||
 | 
					            <LinearLayout
 | 
				
			||||||
 | 
					                android:id="@+id/pageNumbersContainer"
 | 
				
			||||||
 | 
					                android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                android:orientation="horizontal"
 | 
				
			||||||
 | 
					                android:gravity="center"
 | 
				
			||||||
 | 
					                android:layout_marginLeft="8dp"
 | 
				
			||||||
 | 
					                android:layout_marginRight="8dp" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- Next Page Button -->
 | 
				
			||||||
 | 
					            <ImageButton
 | 
				
			||||||
 | 
					                android:id="@+id/btnNextPage"
 | 
				
			||||||
 | 
					                android:layout_width="44dp"
 | 
				
			||||||
 | 
					                android:layout_height="44dp"
 | 
				
			||||||
 | 
					                android:src="@android:drawable/ic_media_ff"
 | 
				
			||||||
 | 
					                android:contentDescription="Next Page"
 | 
				
			||||||
 | 
					                android:layout_marginStart="12dp"
 | 
				
			||||||
 | 
					                android:scaleType="centerInside" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- Last Page Button -->
 | 
				
			||||||
 | 
					            <ImageButton
 | 
				
			||||||
 | 
					                android:id="@+id/btnLastPage"
 | 
				
			||||||
 | 
					                android:layout_width="44dp"
 | 
				
			||||||
 | 
					                android:layout_height="44dp"
 | 
				
			||||||
 | 
					                android:src="@android:drawable/ic_media_next"
 | 
				
			||||||
 | 
					                android:contentDescription="Last Page"
 | 
				
			||||||
 | 
					                android:layout_marginStart="8dp"
 | 
				
			||||||
 | 
					                android:scaleType="centerInside" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    </LinearLayout>
 | 
					    </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,23 +1,23 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="utf-8"?>
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 | 
					<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
    xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
					 | 
				
			||||||
    android:layout_width="match_parent"
 | 
					    android:layout_width="match_parent"
 | 
				
			||||||
    android:layout_height="wrap_content"
 | 
					    android:layout_height="wrap_content"
 | 
				
			||||||
    android:orientation="vertical"
 | 
					    android:orientation="vertical"
 | 
				
			||||||
    android:background="@android:color/white">
 | 
					    android:id="@+id/itemContainer">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <LinearLayout
 | 
					    <LinearLayout
 | 
				
			||||||
        android:layout_width="match_parent"
 | 
					        android:layout_width="match_parent"
 | 
				
			||||||
        android:layout_height="wrap_content"
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
        android:orientation="horizontal"
 | 
					        android:orientation="horizontal"
 | 
				
			||||||
        android:padding="16dp"
 | 
					        android:padding="16dp"
 | 
				
			||||||
        android:gravity="center_vertical">
 | 
					        android:gravity="center_vertical"
 | 
				
			||||||
 | 
					        android:minHeight="64dp">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <!-- Kolom 1: Transaction Info -->
 | 
					        <!-- Column 1: Transaction Info -->
 | 
				
			||||||
        <LinearLayout
 | 
					        <LinearLayout
 | 
				
			||||||
            android:layout_width="0dp"
 | 
					            android:layout_width="0dp"
 | 
				
			||||||
            android:layout_height="wrap_content"
 | 
					            android:layout_height="wrap_content"
 | 
				
			||||||
            android:layout_weight="1"
 | 
					            android:layout_weight="2"
 | 
				
			||||||
            android:orientation="vertical">
 | 
					            android:orientation="vertical">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <!-- Reference ID -->
 | 
					            <!-- Reference ID -->
 | 
				
			||||||
@ -28,16 +28,17 @@
 | 
				
			|||||||
                android:text="ref-eowu3pin"
 | 
					                android:text="ref-eowu3pin"
 | 
				
			||||||
                android:textColor="#333333"
 | 
					                android:textColor="#333333"
 | 
				
			||||||
                android:textSize="16sp"
 | 
					                android:textSize="16sp"
 | 
				
			||||||
                android:textStyle="bold" />
 | 
					                android:textStyle="bold"
 | 
				
			||||||
 | 
					                android:layout_marginBottom="4dp" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <!-- Status and Payment Method -->
 | 
					            <!-- Status, Payment Method, and Created At Row -->
 | 
				
			||||||
            <LinearLayout
 | 
					            <LinearLayout
 | 
				
			||||||
                android:layout_width="wrap_content"
 | 
					                android:layout_width="wrap_content"
 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
                android:orientation="horizontal"
 | 
					                android:orientation="horizontal"
 | 
				
			||||||
                android:layout_marginTop="4dp"
 | 
					 | 
				
			||||||
                android:gravity="center_vertical">
 | 
					                android:gravity="center_vertical">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- Status -->
 | 
				
			||||||
                <TextView
 | 
					                <TextView
 | 
				
			||||||
                    android:id="@+id/textStatus"
 | 
					                    android:id="@+id/textStatus"
 | 
				
			||||||
                    android:layout_width="wrap_content"
 | 
					                    android:layout_width="wrap_content"
 | 
				
			||||||
@ -45,75 +46,73 @@
 | 
				
			|||||||
                    android:text="SUCCESS"
 | 
					                    android:text="SUCCESS"
 | 
				
			||||||
                    android:textSize="12sp"
 | 
					                    android:textSize="12sp"
 | 
				
			||||||
                    android:textStyle="bold"
 | 
					                    android:textStyle="bold"
 | 
				
			||||||
                    android:textColor="#4CAF50" />
 | 
					                    android:textColor="@android:color/white"
 | 
				
			||||||
 | 
					                    android:paddingLeft="8dp"
 | 
				
			||||||
                <TextView
 | 
					                    android:paddingRight="8dp"
 | 
				
			||||||
                    android:layout_width="wrap_content"
 | 
					                    android:paddingTop="2dp"
 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					                    android:paddingBottom="2dp" />
 | 
				
			||||||
                    android:text=" • "
 | 
					 | 
				
			||||||
                    android:textSize="12sp"
 | 
					 | 
				
			||||||
                    android:textColor="#999999"
 | 
					 | 
				
			||||||
                    android:layout_marginHorizontal="4dp" />
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- Payment Method -->
 | 
				
			||||||
                <TextView
 | 
					                <TextView
 | 
				
			||||||
                    android:id="@+id/textPaymentMethod"
 | 
					                    android:id="@+id/textPaymentMethod"
 | 
				
			||||||
                    android:layout_width="wrap_content"
 | 
					                    android:layout_width="wrap_content"
 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
                    android:text="QRIS"
 | 
					                    android:text="QRIS"
 | 
				
			||||||
                    android:textSize="12sp"
 | 
					                    android:textSize="12sp"
 | 
				
			||||||
                    android:textColor="#333333" />
 | 
					                    android:textColor="#666666"
 | 
				
			||||||
 | 
					                    android:layout_marginStart="12dp" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            </LinearLayout>
 | 
					            </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- Created At Date -->
 | 
				
			||||||
 | 
					            <TextView
 | 
				
			||||||
 | 
					                android:id="@+id/textCreatedAt"
 | 
				
			||||||
 | 
					                android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                android:text="01 Jan 2024 14:30"
 | 
				
			||||||
 | 
					                android:textSize="11sp"
 | 
				
			||||||
 | 
					                android:textColor="#999999"
 | 
				
			||||||
 | 
					                android:layout_marginTop="2dp" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        </LinearLayout>
 | 
					        </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <!-- Kolom 2: Amount -->
 | 
					        <!-- Column 2: Amount -->
 | 
				
			||||||
        <TextView
 | 
					        <TextView
 | 
				
			||||||
            android:id="@+id/textAmount"
 | 
					            android:id="@+id/textAmount"
 | 
				
			||||||
            android:layout_width="0dp"
 | 
					            android:layout_width="0dp"
 | 
				
			||||||
            android:layout_height="wrap_content"
 | 
					            android:layout_height="wrap_content"
 | 
				
			||||||
            android:layout_weight="1"
 | 
					            android:layout_weight="1"
 | 
				
			||||||
            android:text="Rp. 1.111"
 | 
					            android:text="Rp. 1.111"
 | 
				
			||||||
            android:textColor="#666666"
 | 
					            android:textColor="#333333"
 | 
				
			||||||
            android:textSize="14sp"
 | 
					            android:textSize="14sp"
 | 
				
			||||||
            android:textAlignment="textEnd" />
 | 
					            android:textStyle="bold"
 | 
				
			||||||
 | 
					            android:gravity="center" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <!-- Kolom 3: Print Button -->
 | 
					        <!-- Column 3: Print Button -->
 | 
				
			||||||
        <LinearLayout
 | 
					        <LinearLayout
 | 
				
			||||||
            android:id="@+id/printSection"
 | 
					            android:id="@+id/printSection"
 | 
				
			||||||
            android:layout_width="0dp"
 | 
					            android:layout_width="0dp"
 | 
				
			||||||
            android:layout_height="wrap_content"
 | 
					            android:layout_height="wrap_content"
 | 
				
			||||||
            android:layout_weight="1"
 | 
					            android:layout_weight="1"
 | 
				
			||||||
            android:orientation="horizontal"
 | 
					            android:orientation="horizontal"
 | 
				
			||||||
            android:gravity="center_vertical|end"
 | 
					            android:gravity="center"
 | 
				
			||||||
            android:background="?android:attr/selectableItemBackground"
 | 
					            android:background="?android:attr/selectableItemBackground"
 | 
				
			||||||
            android:padding="8dp"
 | 
					            android:padding="8dp"
 | 
				
			||||||
            android:clickable="true"
 | 
					            android:clickable="true"
 | 
				
			||||||
            android:focusable="true">
 | 
					            android:focusable="true">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <ImageView
 | 
					 | 
				
			||||||
                android:layout_width="16dp"
 | 
					 | 
				
			||||||
                android:layout_height="16dp"
 | 
					 | 
				
			||||||
                android:src="@drawable/ic_print"
 | 
					 | 
				
			||||||
                android:layout_marginEnd="8dp"
 | 
					 | 
				
			||||||
                app:tint="#666666" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <TextView
 | 
					            <TextView
 | 
				
			||||||
                android:layout_width="wrap_content"
 | 
					                android:layout_width="wrap_content"
 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
                android:text="Cetak Ulang"
 | 
					                android:text="Cetak Ulang"
 | 
				
			||||||
                android:textColor="#666666"
 | 
					                android:textColor="#666666"
 | 
				
			||||||
                android:textSize="14sp" />
 | 
					                android:textSize="12sp"
 | 
				
			||||||
 | 
					                android:drawableLeft="@android:drawable/ic_menu_edit"
 | 
				
			||||||
 | 
					                android:drawablePadding="4dp"
 | 
				
			||||||
 | 
					                android:gravity="center_vertical" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        </LinearLayout>
 | 
					        </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    </LinearLayout>
 | 
					    </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <!-- Bottom Border -->
 | 
					 | 
				
			||||||
    <View
 | 
					 | 
				
			||||||
        android:id="@+id/bottomBorder"
 | 
					 | 
				
			||||||
        android:layout_width="match_parent"
 | 
					 | 
				
			||||||
        android:layout_height="1dp"
 | 
					 | 
				
			||||||
        android:background="#e0e0e0" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
</LinearLayout>
 | 
					</LinearLayout>
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user