From f908169bcf36659c87a88abbf9573ce888e2110f Mon Sep 17 00:00:00 2001 From: Alex <78383757+Lextical@users.noreply.github.com> Date: Wed, 8 Apr 2026 02:06:07 -0600 Subject: [PATCH] Converted merged fragments to viewbinding --- .../listfragments/AnalyticsFragment.java | 100 +++++----- .../listfragments/StaffFragment.java | 60 +++--- .../detailfragments/RefundFragment.java | 80 ++++---- .../detailfragments/SaleDetailFragment.java | 178 +++++++----------- .../detailfragments/StaffDetailFragment.java | 110 +++++------ 5 files changed, 217 insertions(+), 311 deletions(-) diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AnalyticsFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AnalyticsFragment.java index 54dce187..04b35623 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AnalyticsFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AnalyticsFragment.java @@ -5,9 +5,11 @@ import android.os.Bundle; import android.util.Log; import android.view.*; import android.widget.*; +import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import com.example.petstoremobile.R; import com.example.petstoremobile.api.RetrofitClient; +import com.example.petstoremobile.databinding.FragmentAnalyticsBinding; import com.example.petstoremobile.dtos.PageResponse; import com.example.petstoremobile.dtos.SaleDTO; import com.example.petstoremobile.fragments.ListFragment; @@ -18,23 +20,25 @@ import retrofit2.*; public class AnalyticsFragment extends Fragment { - private TextView tvTotalRevenue, tvTotalTransactions, tvAvgTransaction, tvTotalItems; - private LinearLayout llTopRevenue, llTopQuantity, llPaymentMethods, llEmployeePerformance, llDailyRevenue; - private Button btnRefresh; - private ImageButton hamburger; + private FragmentAnalyticsBinding binding; @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_analytics, container, false); + binding = FragmentAnalyticsBinding.inflate(inflater, container, false); - initViews(view); loadAnalytics(); - btnRefresh.setOnClickListener(v -> loadAnalytics()); - hamburger.setOnClickListener(v -> openDrawer()); + binding.btnRefreshAnalytics.setOnClickListener(v -> loadAnalytics()); + binding.btnHamburgerAnalytics.setOnClickListener(v -> openDrawer()); - return view; + return binding.getRoot(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; } private void openDrawer() { @@ -47,33 +51,19 @@ public class AnalyticsFragment extends Fragment { } } - private void initViews(View v) { - tvTotalRevenue = v.findViewById(R.id.tvTotalRevenue); - tvTotalTransactions = v.findViewById(R.id.tvTotalTransactions); - tvAvgTransaction = v.findViewById(R.id.tvAvgTransaction); - tvTotalItems = v.findViewById(R.id.tvTotalItems); - llTopRevenue = v.findViewById(R.id.llTopRevenue); - llTopQuantity = v.findViewById(R.id.llTopQuantity); - llPaymentMethods = v.findViewById(R.id.llPaymentMethods); - llEmployeePerformance = v.findViewById(R.id.llEmployeePerformance); - llDailyRevenue = v.findViewById(R.id.llDailyRevenue); - btnRefresh = v.findViewById(R.id.btnRefreshAnalytics); - hamburger = v.findViewById(R.id.btnHamburgerAnalytics); - } - private void loadAnalytics() { // Clear all sections - llTopRevenue.removeAllViews(); - llTopQuantity.removeAllViews(); - llPaymentMethods.removeAllViews(); - llEmployeePerformance.removeAllViews(); - llDailyRevenue.removeAllViews(); + binding.llTopRevenue.removeAllViews(); + binding.llTopQuantity.removeAllViews(); + binding.llPaymentMethods.removeAllViews(); + binding.llEmployeePerformance.removeAllViews(); + binding.llDailyRevenue.removeAllViews(); // Show loading - tvTotalRevenue.setText("Loading..."); - tvTotalTransactions.setText("..."); - tvAvgTransaction.setText("..."); - tvTotalItems.setText("..."); + binding.tvTotalRevenue.setText("Loading..."); + binding.tvTotalTransactions.setText("..."); + binding.tvAvgTransaction.setText("..."); + binding.tvTotalItems.setText("..."); RetrofitClient.getSaleApi(requireContext()).getAllSales(0, 1000, null, null, null, "saleDate,desc") .enqueue(new Callback>() { @@ -121,10 +111,10 @@ public class AnalyticsFragment extends Fragment { ? totalRevenue.divide(BigDecimal.valueOf(totalTx), 2, RoundingMode.HALF_UP) : BigDecimal.ZERO; - tvTotalRevenue.setText("$" + totalRevenue.setScale(2, RoundingMode.HALF_UP)); - tvTotalTransactions.setText(String.valueOf(totalTx)); - tvAvgTransaction.setText("$" + avgTx); - tvTotalItems.setText(String.valueOf(totalItems)); + binding.tvTotalRevenue.setText("$" + totalRevenue.setScale(2, RoundingMode.HALF_UP)); + binding.tvTotalTransactions.setText(String.valueOf(totalTx)); + binding.tvAvgTransaction.setText("$" + avgTx); + binding.tvTotalItems.setText(String.valueOf(totalItems)); // ── Top Products by Revenue ─────────────────────────── Map revenueByProduct = new LinkedHashMap<>(); @@ -150,28 +140,28 @@ public class AnalyticsFragment extends Fragment { topRevenue.sort((a, b) -> b.getValue().compareTo(a.getValue())); BigDecimal maxRevenue = topRevenue.isEmpty() ? BigDecimal.ONE : topRevenue.get(0).getValue(); - llTopRevenue.removeAllViews(); + binding.llTopRevenue.removeAllViews(); for (int i = 0; i < Math.min(5, topRevenue.size()); i++) { Map.Entry e = topRevenue.get(i); - addBarRow(llTopRevenue, e.getKey(), "$" + e.getValue().setScale(2, RoundingMode.HALF_UP), + addBarRow(binding.llTopRevenue, e.getKey(), "$" + e.getValue().setScale(2, RoundingMode.HALF_UP), e.getValue().floatValue() / maxRevenue.floatValue(), "#ff6b35"); } if (topRevenue.isEmpty()) - addEmptyRow(llTopRevenue, "No data"); + addEmptyRow(binding.llTopRevenue, "No data"); // Sort by quantity desc, take top 5 List> topQuantity = new ArrayList<>(quantityByProduct.entrySet()); topQuantity.sort((a, b) -> b.getValue() - a.getValue()); int maxQty = topQuantity.isEmpty() ? 1 : topQuantity.get(0).getValue(); - llTopQuantity.removeAllViews(); + binding.llTopQuantity.removeAllViews(); for (int i = 0; i < Math.min(5, topQuantity.size()); i++) { Map.Entry e = topQuantity.get(i); - addBarRow(llTopQuantity, e.getKey(), e.getValue() + " units", + addBarRow(binding.llTopQuantity, e.getKey(), e.getValue() + " units", (float) e.getValue() / maxQty, "#4ecdc4"); } if (topQuantity.isEmpty()) - addEmptyRow(llTopQuantity, "No data"); + addEmptyRow(binding.llTopQuantity, "No data"); // ── Payment Methods ─────────────────────────────────── Map paymentCount = new LinkedHashMap<>(); @@ -183,16 +173,16 @@ public class AnalyticsFragment extends Fragment { int maxPayment = paymentCount.values().stream().max(Integer::compare).orElse(1); String[] paymentColors = { "#1a759f", "#ff9f1c", "#577590", "#90be6d" }; int ci = 0; - llPaymentMethods.removeAllViews(); + binding.llPaymentMethods.removeAllViews(); for (Map.Entry e : paymentCount.entrySet()) { - addBarRow(llPaymentMethods, e.getKey(), + addBarRow(binding.llPaymentMethods, e.getKey(), e.getValue() + " transactions", (float) e.getValue() / maxPayment, paymentColors[ci % paymentColors.length]); ci++; } if (paymentCount.isEmpty()) - addEmptyRow(llPaymentMethods, "No data"); + addEmptyRow(binding.llPaymentMethods, "No data"); // ── Employee Performance ────────────────────────────── Map employeeRevenue = new LinkedHashMap<>(); @@ -206,15 +196,15 @@ public class AnalyticsFragment extends Fragment { empList.sort((a, b) -> b.getValue().compareTo(a.getValue())); BigDecimal maxEmp = empList.isEmpty() ? BigDecimal.ONE : empList.get(0).getValue(); - llEmployeePerformance.removeAllViews(); + binding.llEmployeePerformance.removeAllViews(); for (Map.Entry e : empList) { - addBarRow(llEmployeePerformance, e.getKey(), + addBarRow(binding.llEmployeePerformance, e.getKey(), "$" + e.getValue().setScale(2, RoundingMode.HALF_UP), e.getValue().floatValue() / maxEmp.floatValue(), "#1a759f"); } if (empList.isEmpty()) - addEmptyRow(llEmployeePerformance, "No data"); + addEmptyRow(binding.llEmployeePerformance, "No data"); // ── Daily Revenue (last 7 days) ─────────────────────── Map dailyRevenue = new TreeMap<>(); @@ -247,13 +237,13 @@ public class AnalyticsFragment extends Fragment { if (maxDaily.compareTo(BigDecimal.ZERO) == 0) maxDaily = BigDecimal.ONE; - llDailyRevenue.removeAllViews(); + binding.llDailyRevenue.removeAllViews(); for (Map.Entry e : dailyRevenue.entrySet()) { // Show just MM-DD String label = e.getKey().length() >= 10 ? e.getKey().substring(5) : e.getKey(); - addBarRow(llDailyRevenue, label, + addBarRow(binding.llDailyRevenue, label, "$" + e.getValue().setScale(2, RoundingMode.HALF_UP), e.getValue().floatValue() / maxDaily.floatValue(), "#ff6b35"); @@ -325,10 +315,10 @@ public class AnalyticsFragment extends Fragment { private void showError(String msg) { if (getContext() == null) return; - tvTotalRevenue.setText("Error"); - tvTotalTransactions.setText("—"); - tvAvgTransaction.setText("—"); - tvTotalItems.setText("—"); + binding.tvTotalRevenue.setText("Error"); + binding.tvTotalTransactions.setText("—"); + binding.tvAvgTransaction.setText("—"); + binding.tvTotalItems.setText("—"); Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT).show(); } } diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/StaffFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/StaffFragment.java index 4c17edf4..fbaf8473 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/StaffFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/StaffFragment.java @@ -9,66 +9,54 @@ import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.navigation.fragment.NavHostFragment; import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import com.example.petstoremobile.R; import com.example.petstoremobile.adapters.EmployeeAdapter; import com.example.petstoremobile.api.RetrofitClient; +import com.example.petstoremobile.databinding.FragmentStaffBinding; import com.example.petstoremobile.dtos.EmployeeDTO; import com.example.petstoremobile.dtos.PageResponse; import com.example.petstoremobile.utils.UIUtils; -import com.google.android.material.floatingactionbutton.FloatingActionButton; import java.util.*; import retrofit2.*; public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmployeeClickListener { + private FragmentStaffBinding binding; private List employeeList = new ArrayList<>(); private List filteredList = new ArrayList<>(); private EmployeeAdapter adapter; - private SwipeRefreshLayout swipeRefresh; - private EditText etSearch; - private LinearLayout layoutFilter; @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_staff, container, false); - - layoutFilter = view.findViewById(R.id.layoutFilterStaff); + binding = FragmentStaffBinding.inflate(inflater, container, false); - setupRecyclerView(view); - setupSearch(view); - setupSwipeRefresh(view); + setupRecyclerView(); + setupSearch(); + setupSwipeRefresh(); loadStaff(); - FloatingActionButton fab = view.findViewById(R.id.fabAddStaff); - fab.setOnClickListener(v -> openDetail(-1)); + binding.fabAddStaff.setOnClickListener(v -> openDetail(-1)); - ImageButton hamburger = view.findViewById(R.id.btnHamburgerStaff); - UIUtils.setupHamburgerMenu(hamburger, this); + UIUtils.setupHamburgerMenu(binding.btnHamburgerStaff, this); - ImageButton btnToggleFilter = view.findViewById(R.id.btnToggleFilterStaff); - UIUtils.setupFilterToggle(btnToggleFilter, layoutFilter, etSearch); + UIUtils.setupFilterToggle(binding.btnToggleFilterStaff, binding.layoutFilterStaff, binding.etSearchStaff); - return view; + return binding.getRoot(); } - private void setupRecyclerView(View view) { - RecyclerView rv = view.findViewById(R.id.recyclerViewStaff); + private void setupRecyclerView() { adapter = new EmployeeAdapter(filteredList, this); - rv.setLayoutManager(new LinearLayoutManager(getContext())); - rv.setAdapter(adapter); + binding.recyclerViewStaff.setLayoutManager(new LinearLayoutManager(getContext())); + binding.recyclerViewStaff.setAdapter(adapter); } - private void setupSearch(View view) { - etSearch = view.findViewById(R.id.etSearchStaff); - UIUtils.attachSearch(etSearch, () -> filter(etSearch.getText().toString())); + private void setupSearch() { + UIUtils.attachSearch(binding.etSearchStaff, () -> filter(binding.etSearchStaff.getText().toString())); } - private void setupSwipeRefresh(View view) { - swipeRefresh = view.findViewById(R.id.swipeRefreshStaff); - swipeRefresh.setOnRefreshListener(this::loadStaff); + private void setupSwipeRefresh() { + binding.swipeRefreshStaff.setOnRefreshListener(this::loadStaff); } private void filter(String query) { @@ -90,16 +78,16 @@ public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmploye } private void loadStaff() { - if (swipeRefresh != null) swipeRefresh.setRefreshing(true); + binding.swipeRefreshStaff.setRefreshing(true); RetrofitClient.getEmployeeApi(requireContext()).getAllEmployees(0, 100) .enqueue(new Callback>() { public void onResponse(Call> c, Response> r) { - if (swipeRefresh != null) swipeRefresh.setRefreshing(false); + if (binding != null) binding.swipeRefreshStaff.setRefreshing(false); if (r.isSuccessful() && r.body() != null) { employeeList.clear(); employeeList.addAll(r.body().getContent()); - filter(etSearch != null ? etSearch.getText().toString() : ""); + filter(binding != null ? binding.etSearchStaff.getText().toString() : ""); } else { if (getContext() != null) { Toast.makeText(getContext(), "Failed to load staff", @@ -108,7 +96,7 @@ public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmploye } } public void onFailure(Call> c, Throwable t) { - if (swipeRefresh != null) swipeRefresh.setRefreshing(false); + if (binding != null) binding.swipeRefreshStaff.setRefreshing(false); Log.e("StaffFragment", t.getMessage()); } }); @@ -135,4 +123,10 @@ public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmploye public void onEmployeeClick(int position) { openDetail(position); } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; + } } diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/RefundFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/RefundFragment.java index a90f9c12..3d1c7d3c 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/RefundFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/RefundFragment.java @@ -10,6 +10,7 @@ import androidx.fragment.app.Fragment; import androidx.navigation.fragment.NavHostFragment; import com.example.petstoremobile.R; import com.example.petstoremobile.api.RetrofitClient; +import com.example.petstoremobile.databinding.FragmentRefundBinding; import com.example.petstoremobile.dtos.SaleDTO; import com.example.petstoremobile.dtos.PageResponse; import java.math.BigDecimal; @@ -19,13 +20,7 @@ import retrofit2.*; public class RefundFragment extends Fragment { - private EditText etSaleId; - private Button btnLoadSale, btnProcessRefund, btnBack; - private TextView tvSaleInfo, tvRefundTotal; - private LinearLayout llOriginalItems, llRefundItems; - private LinearLayout cardOriginalItems, cardRefundItems, cardPayment; - private Spinner spinnerPayment; - + private FragmentRefundBinding binding; private SaleDTO currentSale; private List allSales = new ArrayList<>(); @@ -60,8 +55,8 @@ public class RefundFragment extends Fragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_refund, container, false); - initViews(view); + binding = FragmentRefundBinding.inflate(inflater, container, false); + setupSpinner(); loadAllSales(); @@ -69,34 +64,19 @@ public class RefundFragment extends Fragment { Bundle args = getArguments(); if (args != null && args.containsKey("saleId")) { long saleId = args.getLong("saleId"); - etSaleId.setText(String.valueOf(saleId)); + binding.etRefundSaleId.setText(String.valueOf(saleId)); // Auto-load after sales are fetched } - btnLoadSale.setOnClickListener(v -> loadSale()); - btnProcessRefund.setOnClickListener(v -> processRefund()); - btnBack.setOnClickListener(v -> navigateBack()); + binding.btnLoadSale.setOnClickListener(v -> loadSale()); + binding.btnProcessRefund.setOnClickListener(v -> processRefund()); + binding.btnRefundBack.setOnClickListener(v -> navigateBack()); - return view; - } - - private void initViews(View v) { - etSaleId = v.findViewById(R.id.etRefundSaleId); - btnLoadSale = v.findViewById(R.id.btnLoadSale); - btnProcessRefund= v.findViewById(R.id.btnProcessRefund); - btnBack = v.findViewById(R.id.btnRefundBack); - tvSaleInfo = v.findViewById(R.id.tvSaleInfo); - tvRefundTotal = v.findViewById(R.id.tvRefundTotal); - llOriginalItems = v.findViewById(R.id.llOriginalItems); - llRefundItems = v.findViewById(R.id.llRefundItems); - cardOriginalItems = v.findViewById(R.id.cardOriginalItems); - cardRefundItems = v.findViewById(R.id.cardRefundItems); - cardPayment = v.findViewById(R.id.cardPayment); - spinnerPayment = v.findViewById(R.id.spinnerRefundPayment); + return binding.getRoot(); } private void setupSpinner() { - spinnerPayment.setAdapter(new ArrayAdapter<>(requireContext(), + binding.spinnerRefundPayment.setAdapter(new ArrayAdapter<>(requireContext(), android.R.layout.simple_spinner_item, PAYMENT_METHODS)); } @@ -121,7 +101,7 @@ public class RefundFragment extends Fragment { } private void loadSale() { - String idStr = etSaleId.getText().toString().trim(); + String idStr = binding.etRefundSaleId.getText().toString().trim(); if (idStr.isEmpty()) { Toast.makeText(getContext(), "Enter a Sale ID", Toast.LENGTH_SHORT).show(); return; @@ -156,8 +136,8 @@ public class RefundFragment extends Fragment { currentSale = found; // Show sale info - tvSaleInfo.setVisibility(View.VISIBLE); - tvSaleInfo.setText("Sale #" + currentSale.getSaleId() + binding.tvSaleInfo.setVisibility(View.VISIBLE); + binding.tvSaleInfo.setText("Sale #" + currentSale.getSaleId() + " | " + (currentSale.getSaleDate() != null ? currentSale.getSaleDate().substring(0, 10) : "") + " | Employee: " + (currentSale.getEmployeeName() != null @@ -169,7 +149,7 @@ public class RefundFragment extends Fragment { if (currentSale.getPaymentMethod() != null) { for (int i = 0; i < PAYMENT_METHODS.length; i++) { if (PAYMENT_METHODS[i].equalsIgnoreCase(currentSale.getPaymentMethod())) { - spinnerPayment.setSelection(i); break; + binding.spinnerRefundPayment.setSelection(i); break; } } } @@ -187,10 +167,10 @@ public class RefundFragment extends Fragment { refundCart.clear(); // Show cards - cardOriginalItems.setVisibility(View.VISIBLE); - cardRefundItems.setVisibility(View.VISIBLE); - cardPayment.setVisibility(View.VISIBLE); - btnProcessRefund.setVisibility(View.VISIBLE); + binding.cardOriginalItems.setVisibility(View.VISIBLE); + binding.cardRefundItems.setVisibility(View.VISIBLE); + binding.cardPayment.setVisibility(View.VISIBLE); + binding.btnProcessRefund.setVisibility(View.VISIBLE); renderOriginalItems(); renderRefundCart(); @@ -233,10 +213,10 @@ public class RefundFragment extends Fragment { } private void renderOriginalItems() { - llOriginalItems.removeAllViews(); + binding.llOriginalItems.removeAllViews(); // Header - addTableHeader(llOriginalItems); + addTableHeader(binding.llOriginalItems); for (RefundItem item : availableItems) { // Calculate pending in cart @@ -254,23 +234,23 @@ public class RefundFragment extends Fragment { true, // show add button () -> showQuantityDialog(item) ); - llOriginalItems.addView(row); + binding.llOriginalItems.addView(row); } } private void renderRefundCart() { - llRefundItems.removeAllViews(); + binding.llRefundItems.removeAllViews(); if (refundCart.isEmpty()) { TextView empty = new TextView(getContext()); empty.setText("No items added to refund yet"); empty.setTextColor(0xFF888780); empty.setTextSize(13f); - llRefundItems.addView(empty); + binding.llRefundItems.addView(empty); return; } - addTableHeader(llRefundItems); + addTableHeader(binding.llRefundItems); for (RefundItem item : refundCart) { LinearLayout row = buildItemRow( @@ -285,7 +265,7 @@ public class RefundFragment extends Fragment { updateRefundTotal(); } ); - llRefundItems.addView(row); + binding.llRefundItems.addView(row); } } @@ -428,7 +408,7 @@ public class RefundFragment extends Fragment { private void updateRefundTotal() { BigDecimal total = BigDecimal.ZERO; for (RefundItem item : refundCart) total = total.add(item.getTotal()); - tvRefundTotal.setText("Refund Total: $" + total.setScale(2, RoundingMode.HALF_UP)); + binding.tvRefundTotal.setText("Refund Total: $" + total.setScale(2, RoundingMode.HALF_UP)); } private void processRefund() { @@ -442,7 +422,7 @@ public class RefundFragment extends Fragment { return; } - String payment = PAYMENT_METHODS[spinnerPayment.getSelectedItemPosition()]; + String payment = PAYMENT_METHODS[binding.spinnerRefundPayment.getSelectedItemPosition()]; // Confirm dialog BigDecimal total = BigDecimal.ZERO; @@ -507,4 +487,10 @@ public class RefundFragment extends Fragment { private void navigateBack() { NavHostFragment.findNavController(this).popBackStack(); } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; + } } diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/SaleDetailFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/SaleDetailFragment.java index d71b2686..772a74e0 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/SaleDetailFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/SaleDetailFragment.java @@ -5,11 +5,11 @@ import android.util.Log; import android.view.*; import android.widget.*; import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; import androidx.navigation.fragment.NavHostFragment; import com.example.petstoremobile.R; import com.example.petstoremobile.api.*; +import com.example.petstoremobile.databinding.FragmentSaleDetailBinding; import com.example.petstoremobile.dtos.*; import java.math.BigDecimal; import java.util.*; @@ -17,11 +17,7 @@ import retrofit2.*; public class SaleDetailFragment extends Fragment { - private TextView tvMode, tvSaleDetailId, tvTotal; - private Spinner spinnerStore, spinnerCustomer, spinnerPayment, spinnerProduct; - private EditText etQuantity; - private Button btnAddItem, btnSave, btnBack, btnRefund; - private LinearLayout llItems; + private FragmentSaleDetailBinding binding; private boolean viewOnly = false; private long saleId = -1; @@ -36,8 +32,11 @@ public class SaleDetailFragment extends Fragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_sale_detail, container, false); - initViews(view); + binding = FragmentSaleDetailBinding.inflate(inflater, container, false); + + binding.spinnerPaymentMethod.setAdapter(new ArrayAdapter<>(requireContext(), + android.R.layout.simple_spinner_item, PAYMENT_METHODS)); + handleArguments(); if (!viewOnly) { @@ -45,30 +44,11 @@ public class SaleDetailFragment extends Fragment { setupAddItem(); } - btnBack.setOnClickListener(v -> navigateBack()); - btnSave.setOnClickListener(v -> saveSale()); - btnRefund.setOnClickListener(v -> showRefundDialog()); + binding.btnSaleBack.setOnClickListener(v -> navigateBack()); + binding.btnSaveSale.setOnClickListener(v -> saveSale()); + binding.btnRefundSale.setOnClickListener(v -> showRefundDialog()); - return view; - } - - private void initViews(View v) { - tvMode = v.findViewById(R.id.tvSaleMode); - tvSaleDetailId = v.findViewById(R.id.tvSaleDetailId); - tvTotal = v.findViewById(R.id.tvSaleDetailTotal); - spinnerStore = v.findViewById(R.id.spinnerSaleStore); - spinnerCustomer = v.findViewById(R.id.spinnerSaleCustomer); - spinnerPayment = v.findViewById(R.id.spinnerPaymentMethod); - spinnerProduct = v.findViewById(R.id.spinnerSaleProduct); - etQuantity = v.findViewById(R.id.etSaleQuantity); - btnAddItem = v.findViewById(R.id.btnAddItem); - btnSave = v.findViewById(R.id.btnSaveSale); - btnBack = v.findViewById(R.id.btnSaleBack); - btnRefund = v.findViewById(R.id.btnRefundSale); - llItems = v.findViewById(R.id.llSaleItems); - - spinnerPayment.setAdapter(new ArrayAdapter<>(requireContext(), - android.R.layout.simple_spinner_item, PAYMENT_METHODS)); + return binding.getRoot(); } private void handleArguments() { @@ -76,31 +56,31 @@ public class SaleDetailFragment extends Fragment { if (a != null && a.containsKey("saleId")) { saleId = a.getLong("saleId"); viewOnly = a.getBoolean("viewOnly", false); - tvMode.setText("Sale #" + saleId); - tvSaleDetailId.setText("ID: " + saleId); + binding.tvSaleMode.setText("Sale #" + saleId); + binding.tvSaleDetailId.setText("ID: " + saleId); // Show refund button for existing non-refund sales if (!a.getBoolean("isRefund", false)) { - btnRefund.setVisibility(View.VISIBLE); + binding.btnRefundSale.setVisibility(View.VISIBLE); } // Hide save and input controls for view only if (viewOnly) { - btnSave.setVisibility(View.GONE); - spinnerStore.setEnabled(false); - spinnerCustomer.setEnabled(false); - spinnerPayment.setEnabled(false); - spinnerProduct.setEnabled(false); - etQuantity.setEnabled(false); - btnAddItem.setEnabled(false); + binding.btnSaveSale.setVisibility(View.GONE); + binding.spinnerSaleStore.setEnabled(false); + binding.spinnerSaleCustomer.setEnabled(false); + binding.spinnerPaymentMethod.setEnabled(false); + binding.spinnerSaleProduct.setEnabled(false); + binding.etSaleQuantity.setEnabled(false); + binding.btnAddItem.setEnabled(false); } // Load sale details loadSaleDetails(); } else { - tvMode.setText("New Sale"); - tvSaleDetailId.setVisibility(View.GONE); - btnRefund.setVisibility(View.GONE); + binding.tvSaleMode.setText("New Sale"); + binding.tvSaleDetailId.setVisibility(View.GONE); + binding.btnRefundSale.setVisibility(View.GONE); } } @@ -117,7 +97,7 @@ public class SaleDetailFragment extends Fragment { List names = new ArrayList<>(); names.add("-- Select Store --"); names.add("Downtown Branch"); - spinnerStore.setAdapter(new ArrayAdapter<>(requireContext(), + binding.spinnerSaleStore.setAdapter(new ArrayAdapter<>(requireContext(), android.R.layout.simple_spinner_item, names)); } @@ -132,8 +112,10 @@ public class SaleDetailFragment extends Fragment { names.add("-- No Customer --"); for (CustomerDTO cu : customerList) names.add(cu.getFirstName() + " " + cu.getLastName()); - spinnerCustomer.setAdapter(new ArrayAdapter<>(requireContext(), - android.R.layout.simple_spinner_item, names)); + if (binding != null) { + binding.spinnerSaleCustomer.setAdapter(new ArrayAdapter<>(requireContext(), + android.R.layout.simple_spinner_item, names)); + } } } @@ -154,8 +136,10 @@ public class SaleDetailFragment extends Fragment { names.add("-- Select Product --"); for (ProductDTO p : productList) names.add(p.getProdName()); - spinnerProduct.setAdapter(new ArrayAdapter<>(requireContext(), - android.R.layout.simple_spinner_item, names)); + if (binding != null) { + binding.spinnerSaleProduct.setAdapter(new ArrayAdapter<>(requireContext(), + android.R.layout.simple_spinner_item, names)); + } } } @@ -171,14 +155,16 @@ public class SaleDetailFragment extends Fragment { public void onResponse(Call c, Response r) { if (r.isSuccessful() && r.body() != null) { SaleDTO sale = r.body(); - tvTotal.setText("Total: $" + sale.getTotalAmount()); - // Display items - if (sale.getItems() != null) { - llItems.removeAllViews(); - for (SaleDTO.SaleItemDTO item : sale.getItems()) { - addItemRow(item.getProductName(), - Math.abs(item.getQuantity()), - item.getUnitPrice()); + if (binding != null) { + binding.tvSaleDetailTotal.setText("Total: $" + sale.getTotalAmount()); + // Display items + if (sale.getItems() != null) { + binding.llSaleItems.removeAllViews(); + for (SaleDTO.SaleItemDTO item : sale.getItems()) { + addItemRow(item.getProductName(), + Math.abs(item.getQuantity()), + item.getUnitPrice()); + } } } } @@ -191,25 +177,25 @@ public class SaleDetailFragment extends Fragment { } private void setupAddItem() { - btnAddItem.setOnClickListener(v -> { - if (spinnerProduct.getSelectedItemPosition() == 0) { + binding.btnAddItem.setOnClickListener(v -> { + if (binding.spinnerSaleProduct.getSelectedItemPosition() == 0) { Toast.makeText(getContext(), "Select a product", Toast.LENGTH_SHORT).show(); return; } - String qtyStr = etQuantity.getText().toString().trim(); + String qtyStr = binding.etSaleQuantity.getText().toString().trim(); if (qtyStr.isEmpty()) { - etQuantity.setError("Enter quantity"); + binding.etSaleQuantity.setError("Enter quantity"); return; } int qty; try { qty = Integer.parseInt(qtyStr); } catch (Exception e) { - etQuantity.setError("Invalid quantity"); + binding.etSaleQuantity.setError("Invalid quantity"); return; } - ProductDTO product = productList.get(spinnerProduct.getSelectedItemPosition() - 1); + ProductDTO product = productList.get(binding.spinnerSaleProduct.getSelectedItemPosition() - 1); // Check if product already in cart for (SaleDTO.SaleItemDTO existing : cartItems) { @@ -223,7 +209,7 @@ public class SaleDetailFragment extends Fragment { cartItems.add(item); addItemRow(product.getProdName(), qty, product.getProdPrice()); updateTotal(); - etQuantity.setText(""); + binding.etSaleQuantity.setText(""); }); } @@ -250,28 +236,25 @@ public class SaleDetailFragment extends Fragment { row.addView(tvName); row.addView(tvQty); row.addView(tvPrice); - llItems.addView(row); + binding.llSaleItems.addView(row); } private void updateTotal() { BigDecimal total = BigDecimal.ZERO; - int productIdx = 0; for (SaleDTO.SaleItemDTO item : cartItems) { - if (productIdx < productList.size()) { - for (ProductDTO p : productList) { - if (p.getProdId().equals(item.getProdId()) && p.getProdPrice() != null) { - total = total.add(p.getProdPrice() - .multiply(BigDecimal.valueOf(item.getQuantity()))); - break; - } + for (ProductDTO p : productList) { + if (p.getProdId().equals(item.getProdId()) && p.getProdPrice() != null) { + total = total.add(p.getProdPrice() + .multiply(BigDecimal.valueOf(item.getQuantity()))); + break; } } } - tvTotal.setText("Total: $" + total); + binding.tvSaleDetailTotal.setText("Total: $" + total); } private void saveSale() { - if (spinnerStore.getSelectedItemPosition() == 0) { + if (binding.spinnerSaleStore.getSelectedItemPosition() == 0) { Toast.makeText(getContext(), "Select a store", Toast.LENGTH_SHORT).show(); return; } @@ -281,12 +264,12 @@ public class SaleDetailFragment extends Fragment { } StoreDTO store = storeList.get(0); // only one store - String payment = PAYMENT_METHODS[spinnerPayment.getSelectedItemPosition()]; + String payment = PAYMENT_METHODS[binding.spinnerPaymentMethod.getSelectedItemPosition()]; // Optional customer Long customerId = null; - if (spinnerCustomer.getSelectedItemPosition() > 0) { - customerId = customerList.get(spinnerCustomer.getSelectedItemPosition() - 1) + if (binding.spinnerSaleCustomer.getSelectedItemPosition() > 0) { + customerId = customerList.get(binding.spinnerSaleCustomer.getSelectedItemPosition() - 1) .getCustomerId(); } @@ -298,11 +281,6 @@ public class SaleDetailFragment extends Fragment { null, customerId); - Log.d("SALE_SAVE", "storeId=" + store.getStoreId() - + " payment=" + payment - + " items=" + cartItems.size() - + " customerId=" + customerId); - RetrofitClient.getSaleApi(requireContext()).createSale(dto) .enqueue(new Callback() { public void onResponse(Call c, Response r) { @@ -334,35 +312,13 @@ public class SaleDetailFragment extends Fragment { NavHostFragment.findNavController(this).navigate(R.id.nav_refund, args); } - private void submitRefund() { - RefundDTO refundDTO = new RefundDTO(saleId, "Refund requested from mobile app"); - RetrofitClient.getRefundApi(requireContext()).createRefund(refundDTO) - .enqueue(new Callback() { - public void onResponse(Call c, Response r) { - if (r.isSuccessful()) { - Toast.makeText(getContext(), "Refund request submitted!", - Toast.LENGTH_SHORT).show(); - btnRefund.setVisibility(View.GONE); - } else { - try { - String err = r.errorBody().string(); - Log.e("REFUND", "Error: " + err); - Toast.makeText(getContext(), "Error: " + err, - Toast.LENGTH_LONG).show(); - } catch (Exception e) { - Log.e("REFUND", "Failed to read error"); - } - } - } - - public void onFailure(Call c, Throwable t) { - Log.e("REFUND", "Failure: " + t.getMessage()); - Toast.makeText(getContext(), "Network error", Toast.LENGTH_SHORT).show(); - } - }); - } - private void navigateBack() { NavHostFragment.findNavController(this).popBackStack(); } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; + } } diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/StaffDetailFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/StaffDetailFragment.java index 2750db72..1200403c 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/StaffDetailFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/StaffDetailFragment.java @@ -11,16 +11,13 @@ import androidx.navigation.fragment.NavHostFragment; import com.example.petstoremobile.R; import com.example.petstoremobile.api.EmployeeApi; import com.example.petstoremobile.api.RetrofitClient; +import com.example.petstoremobile.databinding.FragmentStaffDetailBinding; import com.example.petstoremobile.dtos.EmployeeDTO; import retrofit2.*; public class StaffDetailFragment extends Fragment { - private TextView tvMode, tvStaffId; - private EditText etUsername, etPassword, etFirstName, etLastName, etEmail, etPhone; - private Spinner spinnerRole, spinnerStatus; - private Button btnSave, btnDelete, btnBack; - + private FragmentStaffDetailBinding binding; private long employeeId = -1; private boolean isEditing = false; @@ -30,37 +27,21 @@ public class StaffDetailFragment extends Fragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_staff_detail, container, false); - initViews(view); + binding = FragmentStaffDetailBinding.inflate(inflater, container, false); + setupSpinners(); handleArguments(); - btnBack.setOnClickListener(v -> navigateBack()); - btnSave.setOnClickListener(v -> save()); - btnDelete.setOnClickListener(v -> confirmDelete()); - return view; - } - - private void initViews(View v) { - tvMode = v.findViewById(R.id.tvStaffMode); - tvStaffId = v.findViewById(R.id.tvStaffId); - etUsername = v.findViewById(R.id.etStaffUsername); - etPassword = v.findViewById(R.id.etStaffPassword); - etFirstName = v.findViewById(R.id.etStaffFirstName); - etLastName = v.findViewById(R.id.etStaffLastName); - etEmail = v.findViewById(R.id.etStaffEmail); - etPhone = v.findViewById(R.id.etStaffPhone); - spinnerRole = v.findViewById(R.id.spinnerStaffRole); - spinnerStatus = v.findViewById(R.id.spinnerStaffStatus); - btnSave = v.findViewById(R.id.btnSaveStaff); - btnDelete = v.findViewById(R.id.btnDeleteStaff); - btnBack = v.findViewById(R.id.btnStaffBack); + binding.btnStaffBack.setOnClickListener(v -> navigateBack()); + binding.btnSaveStaff.setOnClickListener(v -> save()); + binding.btnDeleteStaff.setOnClickListener(v -> confirmDelete()); + return binding.getRoot(); } private void setupSpinners() { - spinnerRole.setAdapter(new ArrayAdapter<>(requireContext(), + binding.spinnerStaffRole.setAdapter(new ArrayAdapter<>(requireContext(), android.R.layout.simple_spinner_item, ROLES)); - spinnerStatus.setAdapter(new ArrayAdapter<>(requireContext(), + binding.spinnerStaffStatus.setAdapter(new ArrayAdapter<>(requireContext(), android.R.layout.simple_spinner_item, STATUSES)); } @@ -70,61 +51,61 @@ public class StaffDetailFragment extends Fragment { isEditing = true; employeeId = a.getLong("employeeId", -1); - tvMode.setText("Edit Staff Account"); - tvStaffId.setText("ID: " + employeeId); - tvStaffId.setVisibility(View.VISIBLE); - etUsername.setText(a.getString("username", "")); - etFirstName.setText(a.getString("firstName", "")); - etLastName.setText(a.getString("lastName", "")); - etEmail.setText(a.getString("email", "")); // ← was showing fullName - etPhone.setText(a.getString("phone", "")); - btnDelete.setVisibility(View.VISIBLE); + binding.tvStaffMode.setText("Edit Staff Account"); + binding.tvStaffId.setText("ID: " + employeeId); + binding.tvStaffId.setVisibility(View.VISIBLE); + binding.etStaffUsername.setText(a.getString("username", "")); + binding.etStaffFirstName.setText(a.getString("firstName", "")); + binding.etStaffLastName.setText(a.getString("lastName", "")); + binding.etStaffEmail.setText(a.getString("email", "")); + binding.etStaffPhone.setText(a.getString("phone", "")); + binding.btnDeleteStaff.setVisibility(View.VISIBLE); // Pre-fill role String role = a.getString("role", "STAFF"); for (int i = 0; i < ROLES.length; i++) { if (ROLES[i].equals(role)) { - spinnerRole.setSelection(i); + binding.spinnerStaffRole.setSelection(i); break; } } // Pre-fill status boolean active = a.getBoolean("active", true); - spinnerStatus.setSelection(active ? 0 : 1); + binding.spinnerStaffStatus.setSelection(active ? 0 : 1); } else { isEditing = false; employeeId = -1; - tvMode.setText("Add Staff Account"); - btnDelete.setVisibility(View.GONE); - tvStaffId.setVisibility(View.GONE); + binding.tvStaffMode.setText("Add Staff Account"); + binding.btnDeleteStaff.setVisibility(View.GONE); + binding.tvStaffId.setVisibility(View.GONE); } } private void save() { - String username = etUsername.getText() != null ? etUsername.getText().toString().trim() : ""; - String password = etPassword.getText() != null ? etPassword.getText().toString().trim() : ""; - String firstName = etFirstName.getText() != null ? etFirstName.getText().toString().trim() : ""; - String lastName = etLastName.getText() != null ? etLastName.getText().toString().trim() : ""; - String email = etEmail.getText() != null ? etEmail.getText().toString().trim() : ""; - String phone = etPhone.getText() != null ? etPhone.getText().toString().trim() : ""; - String role = ROLES[spinnerRole.getSelectedItemPosition()]; - boolean active = spinnerStatus.getSelectedItemPosition() == 0; + String username = binding.etStaffUsername.getText() != null ? binding.etStaffUsername.getText().toString().trim() : ""; + String password = binding.etStaffPassword.getText() != null ? binding.etStaffPassword.getText().toString().trim() : ""; + String firstName = binding.etStaffFirstName.getText() != null ? binding.etStaffFirstName.getText().toString().trim() : ""; + String lastName = binding.etStaffLastName.getText() != null ? binding.etStaffLastName.getText().toString().trim() : ""; + String email = binding.etStaffEmail.getText() != null ? binding.etStaffEmail.getText().toString().trim() : ""; + String phone = binding.etStaffPhone.getText() != null ? binding.etStaffPhone.getText().toString().trim() : ""; + String role = ROLES[binding.spinnerStaffRole.getSelectedItemPosition()]; + boolean active = binding.spinnerStaffStatus.getSelectedItemPosition() == 0; // Validation - if (username.isEmpty()) { etUsername.setError("Required"); return; } + if (username.isEmpty()) { binding.etStaffUsername.setError("Required"); return; } if (!isEditing && password.isEmpty()) { - etPassword.setError("Required for new account"); return; + binding.etStaffPassword.setError("Required for new account"); return; } if (!isEditing && password.length() < 6) { - etPassword.setError("At least 6 characters"); return; + binding.etStaffPassword.setError("At least 6 characters"); return; } - if (firstName.isEmpty()) { etFirstName.setError("Required"); return; } - if (lastName.isEmpty()) { etLastName.setError("Required"); return; } - if (email.isEmpty()) { etEmail.setError("Required"); return; } - if (phone.isEmpty()) { etPhone.setError("Required"); return; } + if (firstName.isEmpty()) { binding.etStaffFirstName.setError("Required"); return; } + if (lastName.isEmpty()) { binding.etStaffLastName.setError("Required"); return; } + if (email.isEmpty()) { binding.etStaffEmail.setError("Required"); return; } + if (phone.isEmpty()) { binding.etStaffPhone.setError("Required"); return; } EmployeeDTO dto = new EmployeeDTO( username, @@ -137,10 +118,6 @@ public class StaffDetailFragment extends Fragment { active ); - Log.d("STAFF_SAVE", "isEditing=" + isEditing - + " employeeId=" + employeeId - + " username=" + username); - EmployeeApi api = RetrofitClient.getEmployeeApi(requireContext()); if (isEditing && employeeId > 0) { api.updateEmployee(employeeId, dto).enqueue(simpleCallback("Updated successfully")); @@ -152,14 +129,12 @@ public class StaffDetailFragment extends Fragment { private Callback simpleCallback(String msg) { return new Callback<>() { public void onResponse(Call c, Response r) { - Log.d("STAFF_SAVE", "Response: " + r.code()); if (r.isSuccessful()) { Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT).show(); navigateBack(); } else { try { String err = r.errorBody().string(); - Log.e("STAFF_SAVE", "Error: " + err); Toast.makeText(getContext(), "Error " + r.code() + ": " + err, Toast.LENGTH_LONG).show(); } catch (Exception e) { @@ -168,7 +143,6 @@ public class StaffDetailFragment extends Fragment { } } public void onFailure(Call c, Throwable t) { - Log.e("STAFF_SAVE", "Failure: " + t.getMessage()); Toast.makeText(getContext(), "Network error", Toast.LENGTH_SHORT).show(); } }; @@ -196,4 +170,10 @@ public class StaffDetailFragment extends Fragment { private void navigateBack() { NavHostFragment.findNavController(this).popBackStack(); } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; + } }