converted new fragments to use hilt, MVVM and jetpack nav
This commit is contained in:
@@ -7,30 +7,33 @@ import android.view.*;
|
|||||||
import android.widget.*;
|
import android.widget.*;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import com.example.petstoremobile.R;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
import com.example.petstoremobile.api.RetrofitClient;
|
|
||||||
import com.example.petstoremobile.databinding.FragmentAnalyticsBinding;
|
import com.example.petstoremobile.databinding.FragmentAnalyticsBinding;
|
||||||
import com.example.petstoremobile.dtos.PageResponse;
|
|
||||||
import com.example.petstoremobile.dtos.SaleDTO;
|
import com.example.petstoremobile.dtos.SaleDTO;
|
||||||
import com.example.petstoremobile.fragments.ListFragment;
|
import com.example.petstoremobile.utils.UIUtils;
|
||||||
|
import com.example.petstoremobile.viewmodels.SaleViewModel;
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import retrofit2.*;
|
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
public class AnalyticsFragment extends Fragment {
|
public class AnalyticsFragment extends Fragment {
|
||||||
|
|
||||||
private FragmentAnalyticsBinding binding;
|
private FragmentAnalyticsBinding binding;
|
||||||
|
private SaleViewModel saleViewModel;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
binding = FragmentAnalyticsBinding.inflate(inflater, container, false);
|
binding = FragmentAnalyticsBinding.inflate(inflater, container, false);
|
||||||
|
saleViewModel = new ViewModelProvider(this).get(SaleViewModel.class);
|
||||||
|
|
||||||
loadAnalytics();
|
loadAnalytics();
|
||||||
|
|
||||||
binding.btnRefreshAnalytics.setOnClickListener(v -> loadAnalytics());
|
binding.btnRefreshAnalytics.setOnClickListener(v -> loadAnalytics());
|
||||||
binding.btnHamburgerAnalytics.setOnClickListener(v -> openDrawer());
|
|
||||||
|
UIUtils.setupHamburgerMenu(binding.btnHamburgerAnalytics, this);
|
||||||
|
|
||||||
return binding.getRoot();
|
return binding.getRoot();
|
||||||
}
|
}
|
||||||
@@ -41,16 +44,6 @@ public class AnalyticsFragment extends Fragment {
|
|||||||
binding = null;
|
binding = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openDrawer() {
|
|
||||||
Fragment parent = getParentFragment();
|
|
||||||
if (parent != null) {
|
|
||||||
Fragment grandParent = parent.getParentFragment();
|
|
||||||
if (grandParent instanceof ListFragment) {
|
|
||||||
((ListFragment) grandParent).openDrawer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadAnalytics() {
|
private void loadAnalytics() {
|
||||||
// Clear all sections
|
// Clear all sections
|
||||||
binding.llTopRevenue.removeAllViews();
|
binding.llTopRevenue.removeAllViews();
|
||||||
@@ -65,21 +58,24 @@ public class AnalyticsFragment extends Fragment {
|
|||||||
binding.tvAvgTransaction.setText("...");
|
binding.tvAvgTransaction.setText("...");
|
||||||
binding.tvTotalItems.setText("...");
|
binding.tvTotalItems.setText("...");
|
||||||
|
|
||||||
RetrofitClient.getSaleApi(requireContext()).getAllSales(0, 1000, null, null, null, "saleDate,desc")
|
saleViewModel.getAllSales(0, 1000, null, null, null, "saleDate,desc")
|
||||||
.enqueue(new Callback<PageResponse<SaleDTO>>() {
|
.observe(getViewLifecycleOwner(), resource -> {
|
||||||
public void onResponse(Call<PageResponse<SaleDTO>> c,
|
if (resource != null) {
|
||||||
Response<PageResponse<SaleDTO>> r) {
|
switch (resource.status) {
|
||||||
if (r.isSuccessful() && r.body() != null) {
|
case SUCCESS:
|
||||||
computeAndDisplay(r.body().getContent());
|
if (resource.data != null) {
|
||||||
} else {
|
computeAndDisplay(resource.data.getContent());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ERROR:
|
||||||
|
Log.e("Analytics", resource.message != null ? resource.message : "Error loading sales");
|
||||||
showError("Failed to load sales data");
|
showError("Failed to load sales data");
|
||||||
|
break;
|
||||||
|
case LOADING:
|
||||||
|
// Already showing loading state in UI
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onFailure(Call<PageResponse<SaleDTO>> c, Throwable t) {
|
|
||||||
Log.e("Analytics", t.getMessage());
|
|
||||||
showError("Network error");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,13 +246,12 @@ public class AnalyticsFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a horizontal bar row with label, value and a proportional bar
|
|
||||||
private void addBarRow(LinearLayout parent, String label, String value, float ratio, String color) {
|
private void addBarRow(LinearLayout parent, String label, String value, float ratio, String color) {
|
||||||
|
if (getContext() == null) return;
|
||||||
LinearLayout row = new LinearLayout(getContext());
|
LinearLayout row = new LinearLayout(getContext());
|
||||||
row.setOrientation(LinearLayout.VERTICAL);
|
row.setOrientation(LinearLayout.VERTICAL);
|
||||||
row.setPadding(0, 6, 0, 6);
|
row.setPadding(0, 6, 0, 6);
|
||||||
|
|
||||||
// Label + value row
|
|
||||||
LinearLayout labelRow = new LinearLayout(getContext());
|
LinearLayout labelRow = new LinearLayout(getContext());
|
||||||
labelRow.setOrientation(LinearLayout.HORIZONTAL);
|
labelRow.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
|
|
||||||
@@ -276,7 +271,6 @@ public class AnalyticsFragment extends Fragment {
|
|||||||
labelRow.addView(tvLabel);
|
labelRow.addView(tvLabel);
|
||||||
labelRow.addView(tvValue);
|
labelRow.addView(tvValue);
|
||||||
|
|
||||||
// Bar background
|
|
||||||
LinearLayout barBg = new LinearLayout(getContext());
|
LinearLayout barBg = new LinearLayout(getContext());
|
||||||
LinearLayout.LayoutParams bgParams = new LinearLayout.LayoutParams(
|
LinearLayout.LayoutParams bgParams = new LinearLayout.LayoutParams(
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT, 12);
|
LinearLayout.LayoutParams.MATCH_PARENT, 12);
|
||||||
@@ -284,16 +278,13 @@ public class AnalyticsFragment extends Fragment {
|
|||||||
barBg.setLayoutParams(bgParams);
|
barBg.setLayoutParams(bgParams);
|
||||||
barBg.setBackgroundColor(Color.parseColor("#EEEEEE"));
|
barBg.setBackgroundColor(Color.parseColor("#EEEEEE"));
|
||||||
|
|
||||||
// Bar fill
|
|
||||||
View barFill = new View(getContext());
|
View barFill = new View(getContext());
|
||||||
int fillWidth = (int) (ratio * 100);
|
|
||||||
LinearLayout.LayoutParams fillParams = new LinearLayout.LayoutParams(
|
LinearLayout.LayoutParams fillParams = new LinearLayout.LayoutParams(
|
||||||
0, LinearLayout.LayoutParams.MATCH_PARENT, ratio);
|
0, LinearLayout.LayoutParams.MATCH_PARENT, ratio);
|
||||||
barFill.setLayoutParams(fillParams);
|
barFill.setLayoutParams(fillParams);
|
||||||
barFill.setBackgroundColor(Color.parseColor(color));
|
barFill.setBackgroundColor(Color.parseColor(color));
|
||||||
barBg.addView(barFill);
|
barBg.addView(barFill);
|
||||||
|
|
||||||
// Empty space
|
|
||||||
View spacer = new View(getContext());
|
View spacer = new View(getContext());
|
||||||
spacer.setLayoutParams(new LinearLayout.LayoutParams(
|
spacer.setLayoutParams(new LinearLayout.LayoutParams(
|
||||||
0, LinearLayout.LayoutParams.MATCH_PARENT, 1f - ratio));
|
0, LinearLayout.LayoutParams.MATCH_PARENT, 1f - ratio));
|
||||||
@@ -305,6 +296,7 @@ public class AnalyticsFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void addEmptyRow(LinearLayout parent, String message) {
|
private void addEmptyRow(LinearLayout parent, String message) {
|
||||||
|
if (getContext() == null) return;
|
||||||
TextView tv = new TextView(getContext());
|
TextView tv = new TextView(getContext());
|
||||||
tv.setText(message);
|
tv.setText(message);
|
||||||
tv.setTextColor(Color.parseColor("#888780"));
|
tv.setTextColor(Color.parseColor("#888780"));
|
||||||
@@ -313,7 +305,7 @@ public class AnalyticsFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void showError(String msg) {
|
private void showError(String msg) {
|
||||||
if (getContext() == null)
|
if (getContext() == null || binding == null)
|
||||||
return;
|
return;
|
||||||
binding.tvTotalRevenue.setText("Error");
|
binding.tvTotalRevenue.setText("Error");
|
||||||
binding.tvTotalTransactions.setText("—");
|
binding.tvTotalTransactions.setText("—");
|
||||||
|
|||||||
@@ -5,23 +5,24 @@ import android.util.Log;
|
|||||||
import android.view.*;
|
import android.view.*;
|
||||||
import android.widget.*;
|
import android.widget.*;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
import androidx.navigation.fragment.NavHostFragment;
|
import androidx.navigation.fragment.NavHostFragment;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import com.example.petstoremobile.R;
|
import com.example.petstoremobile.R;
|
||||||
import com.example.petstoremobile.adapters.EmployeeAdapter;
|
import com.example.petstoremobile.adapters.EmployeeAdapter;
|
||||||
import com.example.petstoremobile.api.RetrofitClient;
|
|
||||||
import com.example.petstoremobile.databinding.FragmentStaffBinding;
|
import com.example.petstoremobile.databinding.FragmentStaffBinding;
|
||||||
import com.example.petstoremobile.dtos.EmployeeDTO;
|
import com.example.petstoremobile.dtos.EmployeeDTO;
|
||||||
import com.example.petstoremobile.dtos.PageResponse;
|
|
||||||
import com.example.petstoremobile.utils.UIUtils;
|
import com.example.petstoremobile.utils.UIUtils;
|
||||||
|
import com.example.petstoremobile.viewmodels.EmployeeViewModel;
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import retrofit2.*;
|
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmployeeClickListener {
|
public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmployeeClickListener {
|
||||||
|
|
||||||
private FragmentStaffBinding binding;
|
private FragmentStaffBinding binding;
|
||||||
|
private EmployeeViewModel employeeViewModel;
|
||||||
private List<EmployeeDTO> employeeList = new ArrayList<>();
|
private List<EmployeeDTO> employeeList = new ArrayList<>();
|
||||||
private List<EmployeeDTO> filteredList = new ArrayList<>();
|
private List<EmployeeDTO> filteredList = new ArrayList<>();
|
||||||
private EmployeeAdapter adapter;
|
private EmployeeAdapter adapter;
|
||||||
@@ -30,6 +31,7 @@ public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmploye
|
|||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
binding = FragmentStaffBinding.inflate(inflater, container, false);
|
binding = FragmentStaffBinding.inflate(inflater, container, false);
|
||||||
|
employeeViewModel = new ViewModelProvider(this).get(EmployeeViewModel.class);
|
||||||
|
|
||||||
setupRecyclerView();
|
setupRecyclerView();
|
||||||
setupSearch();
|
setupSearch();
|
||||||
@@ -39,7 +41,6 @@ public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmploye
|
|||||||
binding.fabAddStaff.setOnClickListener(v -> openDetail(-1));
|
binding.fabAddStaff.setOnClickListener(v -> openDetail(-1));
|
||||||
|
|
||||||
UIUtils.setupHamburgerMenu(binding.btnHamburgerStaff, this);
|
UIUtils.setupHamburgerMenu(binding.btnHamburgerStaff, this);
|
||||||
|
|
||||||
UIUtils.setupFilterToggle(binding.btnToggleFilterStaff, binding.layoutFilterStaff, binding.etSearchStaff);
|
UIUtils.setupFilterToggle(binding.btnToggleFilterStaff, binding.layoutFilterStaff, binding.etSearchStaff);
|
||||||
|
|
||||||
return binding.getRoot();
|
return binding.getRoot();
|
||||||
@@ -79,26 +80,29 @@ public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmploye
|
|||||||
|
|
||||||
private void loadStaff() {
|
private void loadStaff() {
|
||||||
binding.swipeRefreshStaff.setRefreshing(true);
|
binding.swipeRefreshStaff.setRefreshing(true);
|
||||||
RetrofitClient.getEmployeeApi(requireContext()).getAllEmployees(0, 100)
|
employeeViewModel.getAllEmployees(0, 100).observe(getViewLifecycleOwner(), resource -> {
|
||||||
.enqueue(new Callback<PageResponse<EmployeeDTO>>() {
|
if (resource != null) {
|
||||||
public void onResponse(Call<PageResponse<EmployeeDTO>> c,
|
switch (resource.status) {
|
||||||
Response<PageResponse<EmployeeDTO>> r) {
|
case SUCCESS:
|
||||||
if (binding != null) binding.swipeRefreshStaff.setRefreshing(false);
|
binding.swipeRefreshStaff.setRefreshing(false);
|
||||||
if (r.isSuccessful() && r.body() != null) {
|
if (resource.data != null) {
|
||||||
employeeList.clear();
|
employeeList.clear();
|
||||||
employeeList.addAll(r.body().getContent());
|
employeeList.addAll(resource.data.getContent());
|
||||||
filter(binding != null ? binding.etSearchStaff.getText().toString() : "");
|
filter(binding != null ? binding.etSearchStaff.getText().toString() : "");
|
||||||
} else {
|
}
|
||||||
|
break;
|
||||||
|
case ERROR:
|
||||||
|
binding.swipeRefreshStaff.setRefreshing(false);
|
||||||
if (getContext() != null) {
|
if (getContext() != null) {
|
||||||
Toast.makeText(getContext(), "Failed to load staff",
|
Toast.makeText(getContext(), resource.message != null ? resource.message : "Failed to load staff",
|
||||||
Toast.LENGTH_SHORT).show();
|
Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case LOADING:
|
||||||
|
binding.swipeRefreshStaff.setRefreshing(true);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void onFailure(Call<PageResponse<EmployeeDTO>> c, Throwable t) {
|
|
||||||
if (binding != null) binding.swipeRefreshStaff.setRefreshing(false);
|
|
||||||
Log.e("StaffFragment", t.getMessage());
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,20 +7,22 @@ import android.view.*;
|
|||||||
import android.widget.*;
|
import android.widget.*;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
import androidx.navigation.fragment.NavHostFragment;
|
import androidx.navigation.fragment.NavHostFragment;
|
||||||
import com.example.petstoremobile.R;
|
import com.example.petstoremobile.R;
|
||||||
import com.example.petstoremobile.api.RetrofitClient;
|
|
||||||
import com.example.petstoremobile.databinding.FragmentRefundBinding;
|
import com.example.petstoremobile.databinding.FragmentRefundBinding;
|
||||||
import com.example.petstoremobile.dtos.SaleDTO;
|
import com.example.petstoremobile.dtos.SaleDTO;
|
||||||
import com.example.petstoremobile.dtos.PageResponse;
|
import com.example.petstoremobile.viewmodels.SaleViewModel;
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import retrofit2.*;
|
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
public class RefundFragment extends Fragment {
|
public class RefundFragment extends Fragment {
|
||||||
|
|
||||||
private FragmentRefundBinding binding;
|
private FragmentRefundBinding binding;
|
||||||
|
private SaleViewModel saleViewModel;
|
||||||
private SaleDTO currentSale;
|
private SaleDTO currentSale;
|
||||||
private List<SaleDTO> allSales = new ArrayList<>();
|
private List<SaleDTO> allSales = new ArrayList<>();
|
||||||
|
|
||||||
@@ -56,6 +58,7 @@ public class RefundFragment extends Fragment {
|
|||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
binding = FragmentRefundBinding.inflate(inflater, container, false);
|
binding = FragmentRefundBinding.inflate(inflater, container, false);
|
||||||
|
saleViewModel = new ViewModelProvider(this).get(SaleViewModel.class);
|
||||||
|
|
||||||
setupSpinner();
|
setupSpinner();
|
||||||
loadAllSales();
|
loadAllSales();
|
||||||
@@ -81,21 +84,24 @@ public class RefundFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void loadAllSales() {
|
private void loadAllSales() {
|
||||||
RetrofitClient.getSaleApi(requireContext()).getAllSales(0, 1000, null, null, null, "saleDate,desc")
|
saleViewModel.getAllSales(0, 1000, null, null, null, "saleDate,desc")
|
||||||
.enqueue(new Callback<PageResponse<SaleDTO>>() {
|
.observe(getViewLifecycleOwner(), resource -> {
|
||||||
public void onResponse(Call<PageResponse<SaleDTO>> c,
|
if (resource != null) {
|
||||||
Response<PageResponse<SaleDTO>> r) {
|
switch (resource.status) {
|
||||||
if (r.isSuccessful() && r.body() != null) {
|
case SUCCESS:
|
||||||
allSales = r.body().getContent();
|
if (resource.data != null) {
|
||||||
|
allSales = resource.data.getContent();
|
||||||
// Auto-load if saleId was pre-filled
|
// Auto-load if saleId was pre-filled
|
||||||
Bundle args = getArguments();
|
Bundle args = getArguments();
|
||||||
if (args != null && args.containsKey("saleId")) {
|
if (args != null && args.containsKey("saleId")) {
|
||||||
loadSale();
|
loadSale();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case ERROR:
|
||||||
|
Log.e("Refund", "Failed to load sales: " + resource.message);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
public void onFailure(Call<PageResponse<SaleDTO>> c, Throwable t) {
|
|
||||||
Log.e("Refund", "Failed to load sales: " + t.getMessage());
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -270,6 +276,7 @@ public class RefundFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void addTableHeader(LinearLayout parent) {
|
private void addTableHeader(LinearLayout parent) {
|
||||||
|
if (getContext() == null) return;
|
||||||
LinearLayout header = new LinearLayout(getContext());
|
LinearLayout header = new LinearLayout(getContext());
|
||||||
header.setOrientation(LinearLayout.HORIZONTAL);
|
header.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
header.setPadding(0, 0, 0, 8);
|
header.setPadding(0, 0, 0, 8);
|
||||||
@@ -290,6 +297,7 @@ public class RefundFragment extends Fragment {
|
|||||||
|
|
||||||
private LinearLayout buildItemRow(String name, int qty, BigDecimal unitPrice,
|
private LinearLayout buildItemRow(String name, int qty, BigDecimal unitPrice,
|
||||||
boolean isAdd, Runnable action) {
|
boolean isAdd, Runnable action) {
|
||||||
|
if (getContext() == null) return new LinearLayout(getContext());
|
||||||
LinearLayout row = new LinearLayout(getContext());
|
LinearLayout row = new LinearLayout(getContext());
|
||||||
row.setOrientation(LinearLayout.HORIZONTAL);
|
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
row.setGravity(android.view.Gravity.CENTER_VERTICAL);
|
row.setGravity(android.view.Gravity.CENTER_VERTICAL);
|
||||||
@@ -458,29 +466,24 @@ public class RefundFragment extends Fragment {
|
|||||||
Log.d("REFUND", "Submitting refund for saleId=" + currentSale.getSaleId()
|
Log.d("REFUND", "Submitting refund for saleId=" + currentSale.getSaleId()
|
||||||
+ " items=" + items.size());
|
+ " items=" + items.size());
|
||||||
|
|
||||||
RetrofitClient.getSaleApi(requireContext()).createSale(dto)
|
saleViewModel.createSale(dto).observe(getViewLifecycleOwner(), resource -> {
|
||||||
.enqueue(new Callback<SaleDTO>() {
|
if (resource != null) {
|
||||||
public void onResponse(Call<SaleDTO> c, Response<SaleDTO> r) {
|
switch (resource.status) {
|
||||||
if (r.isSuccessful() && r.body() != null) {
|
case SUCCESS:
|
||||||
|
if (resource.data != null) {
|
||||||
Toast.makeText(getContext(),
|
Toast.makeText(getContext(),
|
||||||
"Refund #" + r.body().getSaleId() + " processed successfully!",
|
"Refund #" + resource.data.getSaleId() + " processed successfully!",
|
||||||
Toast.LENGTH_LONG).show();
|
Toast.LENGTH_LONG).show();
|
||||||
navigateBack();
|
navigateBack();
|
||||||
} else {
|
}
|
||||||
try {
|
break;
|
||||||
String err = r.errorBody().string();
|
case ERROR:
|
||||||
Log.e("REFUND", "Error: " + err);
|
Log.e("REFUND", "Error: " + resource.message);
|
||||||
Toast.makeText(getContext(), "Error: " + err,
|
Toast.makeText(getContext(), "Error: " + resource.message,
|
||||||
Toast.LENGTH_LONG).show();
|
Toast.LENGTH_LONG).show();
|
||||||
} catch (Exception e) {
|
break;
|
||||||
Log.e("REFUND", "Failed to read error");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
public void onFailure(Call<SaleDTO> c, Throwable t) {
|
|
||||||
Log.e("REFUND", "Failure: " + t.getMessage());
|
|
||||||
Toast.makeText(getContext(), "Network error", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,18 +6,24 @@ import android.view.*;
|
|||||||
import android.widget.*;
|
import android.widget.*;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
import androidx.navigation.fragment.NavHostFragment;
|
import androidx.navigation.fragment.NavHostFragment;
|
||||||
import com.example.petstoremobile.R;
|
import com.example.petstoremobile.R;
|
||||||
import com.example.petstoremobile.api.*;
|
|
||||||
import com.example.petstoremobile.databinding.FragmentSaleDetailBinding;
|
import com.example.petstoremobile.databinding.FragmentSaleDetailBinding;
|
||||||
import com.example.petstoremobile.dtos.*;
|
import com.example.petstoremobile.dtos.*;
|
||||||
|
import com.example.petstoremobile.viewmodels.*;
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import retrofit2.*;
|
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
public class SaleDetailFragment extends Fragment {
|
public class SaleDetailFragment extends Fragment {
|
||||||
|
|
||||||
private FragmentSaleDetailBinding binding;
|
private FragmentSaleDetailBinding binding;
|
||||||
|
private SaleViewModel saleViewModel;
|
||||||
|
private StoreViewModel storeViewModel;
|
||||||
|
private CustomerViewModel customerViewModel;
|
||||||
|
private ProductViewModel productViewModel;
|
||||||
|
|
||||||
private boolean viewOnly = false;
|
private boolean viewOnly = false;
|
||||||
private long saleId = -1;
|
private long saleId = -1;
|
||||||
@@ -34,6 +40,11 @@ public class SaleDetailFragment extends Fragment {
|
|||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
binding = FragmentSaleDetailBinding.inflate(inflater, container, false);
|
binding = FragmentSaleDetailBinding.inflate(inflater, container, false);
|
||||||
|
|
||||||
|
saleViewModel = new ViewModelProvider(this).get(SaleViewModel.class);
|
||||||
|
storeViewModel = new ViewModelProvider(this).get(StoreViewModel.class);
|
||||||
|
customerViewModel = new ViewModelProvider(this).get(CustomerViewModel.class);
|
||||||
|
productViewModel = new ViewModelProvider(this).get(ProductViewModel.class);
|
||||||
|
|
||||||
binding.spinnerPaymentMethod.setAdapter(new ArrayAdapter<>(requireContext(),
|
binding.spinnerPaymentMethod.setAdapter(new ArrayAdapter<>(requireContext(),
|
||||||
android.R.layout.simple_spinner_item, PAYMENT_METHODS));
|
android.R.layout.simple_spinner_item, PAYMENT_METHODS));
|
||||||
|
|
||||||
@@ -91,23 +102,36 @@ public class SaleDetailFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void loadStores() {
|
private void loadStores() {
|
||||||
// Hardcoded since store endpoint is admin only
|
|
||||||
|
storeViewModel.getAllStores(0, 50).observe(getViewLifecycleOwner(), resource -> {
|
||||||
|
if (resource != null && resource.status == com.example.petstoremobile.utils.Resource.Status.SUCCESS && resource.data != null) {
|
||||||
|
storeList = resource.data.getContent();
|
||||||
|
List<String> names = new ArrayList<>();
|
||||||
|
names.add("-- Select Store --");
|
||||||
|
for (StoreDTO s : storeList) names.add(s.getStoreName());
|
||||||
|
if (binding != null) {
|
||||||
|
binding.spinnerSaleStore.setAdapter(new ArrayAdapter<>(requireContext(),
|
||||||
|
android.R.layout.simple_spinner_item, names));
|
||||||
|
}
|
||||||
|
} else if (storeList.isEmpty()) {
|
||||||
|
// Fallback if failed or empty
|
||||||
storeList = new ArrayList<>();
|
storeList = new ArrayList<>();
|
||||||
storeList.add(new StoreDTO(1L, "Downtown Branch"));
|
storeList.add(new StoreDTO(1L, "Downtown Branch"));
|
||||||
List<String> names = new ArrayList<>();
|
List<String> names = new ArrayList<>();
|
||||||
names.add("-- Select Store --");
|
names.add("-- Select Store --");
|
||||||
names.add("Downtown Branch");
|
names.add("Downtown Branch");
|
||||||
|
if (binding != null) {
|
||||||
binding.spinnerSaleStore.setAdapter(new ArrayAdapter<>(requireContext(),
|
binding.spinnerSaleStore.setAdapter(new ArrayAdapter<>(requireContext(),
|
||||||
android.R.layout.simple_spinner_item, names));
|
android.R.layout.simple_spinner_item, names));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void loadCustomers() {
|
private void loadCustomers() {
|
||||||
RetrofitClient.getCustomerApi(requireContext()).getAllCustomers(0, 200)
|
customerViewModel.getAllCustomers(0, 200).observe(getViewLifecycleOwner(), resource -> {
|
||||||
.enqueue(new Callback<PageResponse<CustomerDTO>>() {
|
if (resource != null && resource.status == com.example.petstoremobile.utils.Resource.Status.SUCCESS && resource.data != null) {
|
||||||
public void onResponse(Call<PageResponse<CustomerDTO>> c,
|
customerList = resource.data.getContent();
|
||||||
Response<PageResponse<CustomerDTO>> r) {
|
|
||||||
if (r.isSuccessful() && r.body() != null) {
|
|
||||||
customerList = r.body().getContent();
|
|
||||||
List<String> names = new ArrayList<>();
|
List<String> names = new ArrayList<>();
|
||||||
names.add("-- No Customer --");
|
names.add("-- No Customer --");
|
||||||
for (CustomerDTO cu : customerList)
|
for (CustomerDTO cu : customerList)
|
||||||
@@ -117,21 +141,13 @@ public class SaleDetailFragment extends Fragment {
|
|||||||
android.R.layout.simple_spinner_item, names));
|
android.R.layout.simple_spinner_item, names));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void onFailure(Call<PageResponse<CustomerDTO>> c, Throwable t) {
|
|
||||||
Log.e("SaleDetail", "Customer load failed: " + t.getMessage());
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadProducts() {
|
private void loadProducts() {
|
||||||
RetrofitClient.getProductApi(requireContext()).getAllProducts(null, null, 0, 200, null)
|
productViewModel.getAllProducts(null, null, 0, 200, null).observe(getViewLifecycleOwner(), resource -> {
|
||||||
.enqueue(new Callback<PageResponse<ProductDTO>>() {
|
if (resource != null && resource.status == com.example.petstoremobile.utils.Resource.Status.SUCCESS && resource.data != null) {
|
||||||
public void onResponse(Call<PageResponse<ProductDTO>> c,
|
productList = resource.data.getContent();
|
||||||
Response<PageResponse<ProductDTO>> r) {
|
|
||||||
if (r.isSuccessful() && r.body() != null) {
|
|
||||||
productList = r.body().getContent();
|
|
||||||
List<String> names = new ArrayList<>();
|
List<String> names = new ArrayList<>();
|
||||||
names.add("-- Select Product --");
|
names.add("-- Select Product --");
|
||||||
for (ProductDTO p : productList)
|
for (ProductDTO p : productList)
|
||||||
@@ -141,20 +157,13 @@ public class SaleDetailFragment extends Fragment {
|
|||||||
android.R.layout.simple_spinner_item, names));
|
android.R.layout.simple_spinner_item, names));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void onFailure(Call<PageResponse<ProductDTO>> c, Throwable t) {
|
|
||||||
Log.e("SaleDetail", "Product load failed: " + t.getMessage());
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadSaleDetails() {
|
private void loadSaleDetails() {
|
||||||
RetrofitClient.getSaleApi(requireContext()).getSaleById(saleId)
|
saleViewModel.getSaleById(saleId).observe(getViewLifecycleOwner(), resource -> {
|
||||||
.enqueue(new Callback<SaleDTO>() {
|
if (resource != null && resource.status == com.example.petstoremobile.utils.Resource.Status.SUCCESS && resource.data != null) {
|
||||||
public void onResponse(Call<SaleDTO> c, Response<SaleDTO> r) {
|
SaleDTO sale = resource.data;
|
||||||
if (r.isSuccessful() && r.body() != null) {
|
|
||||||
SaleDTO sale = r.body();
|
|
||||||
if (binding != null) {
|
if (binding != null) {
|
||||||
binding.tvSaleDetailTotal.setText("Total: $" + sale.getTotalAmount());
|
binding.tvSaleDetailTotal.setText("Total: $" + sale.getTotalAmount());
|
||||||
// Display items
|
// Display items
|
||||||
@@ -168,11 +177,6 @@ public class SaleDetailFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void onFailure(Call<SaleDTO> c, Throwable t) {
|
|
||||||
Log.e("SaleDetail", "Load failed: " + t.getMessage());
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,6 +218,7 @@ public class SaleDetailFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void addItemRow(String name, int qty, BigDecimal price) {
|
private void addItemRow(String name, int qty, BigDecimal price) {
|
||||||
|
if (getContext() == null) return;
|
||||||
LinearLayout row = new LinearLayout(getContext());
|
LinearLayout row = new LinearLayout(getContext());
|
||||||
row.setOrientation(LinearLayout.HORIZONTAL);
|
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
row.setPadding(0, 8, 0, 8);
|
row.setPadding(0, 8, 0, 8);
|
||||||
@@ -263,7 +268,7 @@ public class SaleDetailFragment extends Fragment {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
StoreDTO store = storeList.get(0); // only one store
|
StoreDTO store = storeList.get(binding.spinnerSaleStore.getSelectedItemPosition() - 1);
|
||||||
String payment = PAYMENT_METHODS[binding.spinnerPaymentMethod.getSelectedItemPosition()];
|
String payment = PAYMENT_METHODS[binding.spinnerPaymentMethod.getSelectedItemPosition()];
|
||||||
|
|
||||||
// Optional customer
|
// Optional customer
|
||||||
@@ -281,28 +286,19 @@ public class SaleDetailFragment extends Fragment {
|
|||||||
null,
|
null,
|
||||||
customerId);
|
customerId);
|
||||||
|
|
||||||
RetrofitClient.getSaleApi(requireContext()).createSale(dto)
|
saleViewModel.createSale(dto).observe(getViewLifecycleOwner(), resource -> {
|
||||||
.enqueue(new Callback<SaleDTO>() {
|
if (resource != null) {
|
||||||
public void onResponse(Call<SaleDTO> c, Response<SaleDTO> r) {
|
switch (resource.status) {
|
||||||
if (r.isSuccessful()) {
|
case SUCCESS:
|
||||||
Toast.makeText(getContext(), "Sale saved!", Toast.LENGTH_SHORT).show();
|
Toast.makeText(getContext(), "Sale saved!", Toast.LENGTH_SHORT).show();
|
||||||
navigateBack();
|
navigateBack();
|
||||||
} else {
|
break;
|
||||||
try {
|
case ERROR:
|
||||||
String err = r.errorBody().string();
|
Log.e("SALE_SAVE", "Error: " + resource.message);
|
||||||
Log.e("SALE_SAVE", "Error: " + err);
|
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_LONG).show();
|
||||||
Toast.makeText(getContext(), "Error " + r.code() + ": " + err,
|
break;
|
||||||
Toast.LENGTH_LONG).show();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e("SALE_SAVE", "Failed to read error");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void onFailure(Call<SaleDTO> c, Throwable t) {
|
|
||||||
Log.e("SALE_SAVE", "Failure: " + t.getMessage());
|
|
||||||
Toast.makeText(getContext(), "Network error", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,17 +7,19 @@ import android.widget.*;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
import androidx.navigation.fragment.NavHostFragment;
|
import androidx.navigation.fragment.NavHostFragment;
|
||||||
import com.example.petstoremobile.R;
|
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.databinding.FragmentStaffDetailBinding;
|
||||||
import com.example.petstoremobile.dtos.EmployeeDTO;
|
import com.example.petstoremobile.dtos.EmployeeDTO;
|
||||||
import retrofit2.*;
|
import com.example.petstoremobile.viewmodels.EmployeeViewModel;
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint;
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
public class StaffDetailFragment extends Fragment {
|
public class StaffDetailFragment extends Fragment {
|
||||||
|
|
||||||
private FragmentStaffDetailBinding binding;
|
private FragmentStaffDetailBinding binding;
|
||||||
|
private EmployeeViewModel employeeViewModel;
|
||||||
private long employeeId = -1;
|
private long employeeId = -1;
|
||||||
private boolean isEditing = false;
|
private boolean isEditing = false;
|
||||||
|
|
||||||
@@ -28,6 +30,7 @@ public class StaffDetailFragment extends Fragment {
|
|||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
binding = FragmentStaffDetailBinding.inflate(inflater, container, false);
|
binding = FragmentStaffDetailBinding.inflate(inflater, container, false);
|
||||||
|
employeeViewModel = new ViewModelProvider(this).get(EmployeeViewModel.class);
|
||||||
|
|
||||||
setupSpinners();
|
setupSpinners();
|
||||||
handleArguments();
|
handleArguments();
|
||||||
@@ -118,34 +121,35 @@ public class StaffDetailFragment extends Fragment {
|
|||||||
active
|
active
|
||||||
);
|
);
|
||||||
|
|
||||||
EmployeeApi api = RetrofitClient.getEmployeeApi(requireContext());
|
|
||||||
if (isEditing && employeeId > 0) {
|
if (isEditing && employeeId > 0) {
|
||||||
api.updateEmployee(employeeId, dto).enqueue(simpleCallback("Updated successfully"));
|
employeeViewModel.updateEmployee(employeeId, dto).observe(getViewLifecycleOwner(), resource -> {
|
||||||
} else {
|
if (resource != null) {
|
||||||
api.createEmployee(dto).enqueue(simpleCallback("Staff account created"));
|
switch (resource.status) {
|
||||||
}
|
case SUCCESS:
|
||||||
}
|
Toast.makeText(getContext(), "Updated successfully", Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
private Callback<EmployeeDTO> simpleCallback(String msg) {
|
|
||||||
return new Callback<>() {
|
|
||||||
public void onResponse(Call<EmployeeDTO> c, Response<EmployeeDTO> r) {
|
|
||||||
if (r.isSuccessful()) {
|
|
||||||
Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT).show();
|
|
||||||
navigateBack();
|
navigateBack();
|
||||||
|
break;
|
||||||
|
case ERROR:
|
||||||
|
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_LONG).show();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
try {
|
employeeViewModel.createEmployee(dto).observe(getViewLifecycleOwner(), resource -> {
|
||||||
String err = r.errorBody().string();
|
if (resource != null) {
|
||||||
Toast.makeText(getContext(), "Error " + r.code() + ": " + err,
|
switch (resource.status) {
|
||||||
Toast.LENGTH_LONG).show();
|
case SUCCESS:
|
||||||
} catch (Exception e) {
|
Toast.makeText(getContext(), "Staff account created", Toast.LENGTH_SHORT).show();
|
||||||
Log.e("STAFF_SAVE", "Failed to read error");
|
navigateBack();
|
||||||
|
break;
|
||||||
|
case ERROR:
|
||||||
|
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_LONG).show();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
public void onFailure(Call<EmployeeDTO> c, Throwable t) {
|
|
||||||
Toast.makeText(getContext(), "Network error", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void confirmDelete() {
|
private void confirmDelete() {
|
||||||
@@ -153,15 +157,17 @@ public class StaffDetailFragment extends Fragment {
|
|||||||
.setTitle("Delete Staff Account?")
|
.setTitle("Delete Staff Account?")
|
||||||
.setMessage("This will permanently delete this staff account.")
|
.setMessage("This will permanently delete this staff account.")
|
||||||
.setPositiveButton("Yes", (d, w) ->
|
.setPositiveButton("Yes", (d, w) ->
|
||||||
RetrofitClient.getEmployeeApi(requireContext())
|
employeeViewModel.deleteEmployee(employeeId).observe(getViewLifecycleOwner(), resource -> {
|
||||||
.deleteEmployee(employeeId)
|
if (resource != null) {
|
||||||
.enqueue(new Callback<Void>() {
|
switch (resource.status) {
|
||||||
public void onResponse(Call<Void> c, Response<Void> r) {
|
case SUCCESS:
|
||||||
navigateBack();
|
navigateBack();
|
||||||
}
|
break;
|
||||||
public void onFailure(Call<Void> c, Throwable t) {
|
case ERROR:
|
||||||
Toast.makeText(getContext(), "Delete failed",
|
Toast.makeText(getContext(), "Delete failed: " + resource.message,
|
||||||
Toast.LENGTH_SHORT).show();
|
Toast.LENGTH_SHORT).show();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.setNegativeButton("No", null).show();
|
.setNegativeButton("No", null).show();
|
||||||
|
|||||||
Reference in New Issue
Block a user