Refactored Andriod project to use MVVM structure (Need to apply this so sales too after merge)

- Used MVVM structure so fragments are not doing all the operation from views to data and calls
- organized the structure of our proejct
This commit is contained in:
Alex
2026-04-04 23:35:38 -06:00
parent 47bd755e72
commit 0c75ffbf35
41 changed files with 2869 additions and 991 deletions

View File

@@ -14,20 +14,17 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.lifecycle.ViewModelProvider;
import com.example.petstoremobile.R;
import com.example.petstoremobile.api.auth.AuthApi;
import com.example.petstoremobile.api.auth.TokenManager;
import com.example.petstoremobile.dtos.AuthDTO;
import com.example.petstoremobile.dtos.UserDTO;
import com.example.petstoremobile.viewmodels.AuthViewModel;
import com.example.petstoremobile.utils.Resource;
import javax.inject.Inject;
import javax.inject.Named;
import dagger.hilt.android.AndroidEntryPoint;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
//The login screen activity
@AndroidEntryPoint
@@ -37,12 +34,11 @@ public class MainActivity extends AppCompatActivity {
private EditText etPassword;
private Button btnLogin;
private TextView tvLoginStatus;
private AuthViewModel viewModel;
@Inject AuthApi authApi;
@Inject TokenManager tokenManager;
@Inject @Named("baseUrl") String baseUrl;
@Override
protected void onCreate(Bundle savedInstanceState) {
EdgeToEdge.enable(this);
@@ -54,8 +50,7 @@ public class MainActivity extends AppCompatActivity {
// If a customer somehow remained logged in, clear them out
tokenManager.clearLoginData();
} else {
Intent intent = new Intent(this, HomeActivity.class);
startActivity(intent);
startActivity(new Intent(this, HomeActivity.class));
finish();
return;
}
@@ -99,92 +94,51 @@ public class MainActivity extends AppCompatActivity {
return;
}
//Call login from api and get response
authApi.login(new AuthDTO.LoginRequest(username,password)).enqueue(new Callback<AuthDTO.LoginResponse>() {
@Override
public void onResponse(Call<AuthDTO.LoginResponse> call, Response<AuthDTO.LoginResponse> response) {
if (response.isSuccessful() && response.body() != null) {
String role = response.body().getRole();
// Check if the user is a CUSTOMER and deny login if so
if ("CUSTOMER".equalsIgnoreCase(role)) {
Toast.makeText(MainActivity.this, "Access denied: Customers are not allowed to log in.", Toast.LENGTH_LONG).show();
tvLoginStatus.setText("Customers are not allowed to log in");
return;
}
//save login data in shared preferences
tokenManager.saveLoginData(
response.body().getToken(),
response.body().getUsername(),
role
);
//fetch user id from api then login to home activity
authApi.getMe()
.enqueue(new Callback<UserDTO>() {
@Override
public void onResponse(Call<UserDTO> call,
Response<UserDTO> response) {
if (response.isSuccessful() && response.body() != null) {
tokenManager.saveUserId(response.body().getId());
}
Toast.makeText(MainActivity.this, "Login successful", Toast.LENGTH_SHORT).show();
startActivity(new Intent(MainActivity.this, HomeActivity.class));
finish();
}
@Override
public void onFailure(Call<UserDTO> call,
Throwable t) {
Log.e("MainActivity", "Failed to fetch userId", t);
Toast.makeText(MainActivity.this, "Login successful", Toast.LENGTH_SHORT).show();
startActivity(new Intent(MainActivity.this, HomeActivity.class));
finish();
}
});
} else {
String errorMessage;
switch (response.code()) {
case 401:
errorMessage = "Invalid username or password";
break;
case 500:
errorMessage = "Server error. Please try again later.";
break;
case 503:
errorMessage = "Service unavailable. Backend may be starting up.";
break;
default:
errorMessage = "Login failed (Error " + response.code() + ")";
}
Toast.makeText(MainActivity.this, errorMessage, Toast.LENGTH_LONG).show();
tvLoginStatus.setText(errorMessage);
}
}
@Override
public void onFailure(Call<AuthDTO.LoginResponse> call, Throwable t) {
Log.e("MainActivity", "Login request failed", t);
String errorMessage;
if (t instanceof java.net.ConnectException ||
t instanceof java.net.SocketTimeoutException ||
t instanceof java.net.UnknownHostException) {
errorMessage = "Cannot connect to server at " + baseUrl +
". Please check if the backend is running.";
} else if (t instanceof java.io.IOException) {
errorMessage = "Network error. Please check your connection.";
} else {
errorMessage = "Login failed: " + t.getMessage();
}
Toast.makeText(MainActivity.this, errorMessage, Toast.LENGTH_LONG).show();
tvLoginStatus.setText(errorMessage);
}
});
performLogin(username, password);
});
}
}
private void performLogin(String username, String password) {
viewModel.login(username, password).observe(this, resource -> {
if (resource == null) return;
switch (resource.status) {
case LOADING:
btnLogin.setEnabled(false);
tvLoginStatus.setText("Logging in...");
break;
case SUCCESS:
if (resource.data != null) {
String role = resource.data.getRole();
if ("CUSTOMER".equalsIgnoreCase(role)) {
btnLogin.setEnabled(true);
tvLoginStatus.setText("Customers are not allowed to log in");
Toast.makeText(this, "Access denied: Customers are not allowed to log in.", Toast.LENGTH_LONG).show();
} else {
tokenManager.saveLoginData(resource.data.getToken(), resource.data.getUsername(), role);
fetchUserIdAndNavigate();
}
}
break;
case ERROR:
btnLogin.setEnabled(true);
tvLoginStatus.setText(resource.message);
Toast.makeText(this, resource.message, Toast.LENGTH_LONG).show();
break;
}
});
}
private void fetchUserIdAndNavigate() {
viewModel.getMe().observe(this, resource -> {
if (resource != null && resource.status != Resource.Status.LOADING) {
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
tokenManager.saveUserId(resource.data.getId());
}
Toast.makeText(this, "Login successful", Toast.LENGTH_SHORT).show();
startActivity(new Intent(this, HomeActivity.class));
finish();
}
});
}
}

View File

@@ -7,17 +7,19 @@ import android.util.Log;
import android.view.*;
import android.widget.*;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
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.AdoptionAdapter;
import com.example.petstoremobile.api.AdoptionApi;
import com.example.petstoremobile.dtos.AdoptionDTO;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.fragments.ListFragment;
import com.example.petstoremobile.viewmodels.AdoptionViewModel;
import com.example.petstoremobile.utils.Resource;
import com.example.petstoremobile.utils.EventDecorator;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.prolificinteractive.materialcalendarview.CalendarDay;
@@ -28,10 +30,7 @@ import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
import retrofit2.*;
@AndroidEntryPoint
public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdoptionClickListener {
@@ -39,7 +38,7 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
private List<AdoptionDTO> adoptionList = new ArrayList<>();
private List<AdoptionDTO> filteredList = new ArrayList<>();
private AdoptionAdapter adapter;
@Inject AdoptionApi api;
private AdoptionViewModel viewModel;
private SwipeRefreshLayout swipeRefresh;
private EditText etSearch;
private ImageButton hamburger;
@@ -50,7 +49,13 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(AdoptionViewModel.class);
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_adoption, container, false);
@@ -177,26 +182,34 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
adapter.notifyDataSetChanged();
}
// Helper function to get a list of all adoptions from the backend
private void loadAdoptions() {
if (swipeRefresh != null) swipeRefresh.setRefreshing(true);
api.getAllAdoptions(0, 500).enqueue(new Callback<PageResponse<AdoptionDTO>>() {
public void onResponse(Call<PageResponse<AdoptionDTO>> c,
Response<PageResponse<AdoptionDTO>> r) {
if (swipeRefresh != null) swipeRefresh.setRefreshing(false);
if (r.isSuccessful() && r.body() != null) {
adoptionList.clear();
adoptionList.addAll(r.body().getContent());
updateCalendarDecorators();
filter(etSearch != null ? etSearch.getText().toString() : "");
} else {
Toast.makeText(getContext(), "Failed to load adoptions", Toast.LENGTH_SHORT).show();
Log.e("AdoptionFragment", "Error: " + r.message());
}
}
public void onFailure(Call<PageResponse<AdoptionDTO>> c, Throwable t) {
if (swipeRefresh != null) swipeRefresh.setRefreshing(false);
Toast.makeText(getContext(), "Network error", Toast.LENGTH_SHORT).show();
Log.e("AdoptionFragment", t.getMessage());
//Load all adoptions from the backend using viewModel
viewModel.getAllAdoptions(0, 500).observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return;
// Check the status to see if the resource is loaded and display the data
switch (resource.status) {
case LOADING:
// Show loading indicator
if (swipeRefresh != null) swipeRefresh.setRefreshing(true);
break;
case SUCCESS:
// Hide loading indicator and display data
if (swipeRefresh != null) swipeRefresh.setRefreshing(false);
if (resource.data != null) {
adoptionList.clear();
adoptionList.addAll(resource.data.getContent());
updateCalendarDecorators();
filter(etSearch != null ? etSearch.getText().toString() : "");
}
break;
case ERROR:
// Hide loading indicator and toast error message
if (swipeRefresh != null) swipeRefresh.setRefreshing(false);
Toast.makeText(getContext(), "Failed to load adoptions: " + resource.message, Toast.LENGTH_SHORT).show();
Log.e("AdoptionFragment", "Error loading adoptions: " + resource.message);
break;
}
});
}

View File

@@ -4,7 +4,9 @@ import android.graphics.Color;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -21,14 +23,14 @@ import android.widget.Toast;
import com.example.petstoremobile.R;
import com.example.petstoremobile.adapters.AppointmentAdapter;
import com.example.petstoremobile.api.AppointmentApi;
import com.example.petstoremobile.api.PetApi;
import com.example.petstoremobile.api.ServiceApi;
import com.example.petstoremobile.dtos.AppointmentDTO;
import com.example.petstoremobile.dtos.ServiceDTO;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.dtos.PetDTO;
import com.example.petstoremobile.fragments.ListFragment;
import com.example.petstoremobile.viewmodels.AppointmentViewModel;
import com.example.petstoremobile.viewmodels.PetViewModel;
import com.example.petstoremobile.viewmodels.ServiceViewModel;
import com.example.petstoremobile.utils.Resource;
import com.example.petstoremobile.utils.EventDecorator;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.prolificinteractive.materialcalendarview.CalendarDay;
@@ -45,12 +47,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
@AndroidEntryPoint
public class AppointmentFragment extends Fragment implements AppointmentAdapter.OnAppointmentClickListener {
@@ -61,9 +58,9 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
private List<ServiceDTO> serviceList = new ArrayList<>();
private AppointmentAdapter adapter;
@Inject AppointmentApi api;
@Inject PetApi petApi;
@Inject ServiceApi serviceApi;
private AppointmentViewModel appointmentViewModel;
private PetViewModel petViewModel;
private ServiceViewModel serviceViewModel;
private SwipeRefreshLayout swipeRefreshLayout;
private EditText etSearch;
@@ -75,7 +72,15 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
appointmentViewModel = new ViewModelProvider(this).get(AppointmentViewModel.class);
petViewModel = new ViewModelProvider(this).get(PetViewModel.class);
serviceViewModel = new ViewModelProvider(this).get(ServiceViewModel.class);
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_appointment, container, false);
@@ -91,7 +96,6 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
loadPets();
loadServices();
FloatingActionButton fabAdd = view.findViewById(R.id.fabAddAppointment);
fabAdd.setOnClickListener(v -> openAppointmentDetails(-1));
@@ -239,76 +243,54 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
openAppointmentDetails(position);
}
// Helper function to get a list of all appointments from the backend
private void loadAppointmentData() {
if (swipeRefreshLayout != null)
swipeRefreshLayout.setRefreshing(true);
api.getAllAppointments(0, 500).enqueue(new Callback<PageResponse<AppointmentDTO>>() {
@Override
public void onResponse(Call<PageResponse<AppointmentDTO>> call,
Response<PageResponse<AppointmentDTO>> response) {
if (swipeRefreshLayout != null)
swipeRefreshLayout.setRefreshing(false);
if (response.isSuccessful() && response.body() != null) {
appointmentList.clear();
appointmentList.addAll(response.body().getContent());
updateCalendarDecorators();
filterAppointments(etSearch != null ? etSearch.getText().toString() : "");
} else {
Log.e("AppointmentFragment", "Error: " + response.message());
Toast.makeText(getContext(), "Failed to load appointments", Toast.LENGTH_SHORT).show();
}
}
//Load all appointments from the backend using viewModel
appointmentViewModel.getAllAppointments(0, 500).observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return;
@Override
public void onFailure(Call<PageResponse<AppointmentDTO>> call, Throwable t) {
if (swipeRefreshLayout != null)
swipeRefreshLayout.setRefreshing(false);
Toast.makeText(getContext(), "Network error: " + t.getMessage(), Toast.LENGTH_SHORT).show();
Log.e("AppointmentFragment", t.getMessage());
// Check the status to see if the resource is loaded and display the data
switch (resource.status) {
case LOADING:
// Show loading indicator
if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(true);
break;
case SUCCESS:
// Hide loading indicator and display data
if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false);
if (resource.data != null) {
appointmentList.clear();
appointmentList.addAll(resource.data.getContent());
updateCalendarDecorators();
filterAppointments(etSearch != null ? etSearch.getText().toString() : "");
}
break;
case ERROR:
// Hide loading indicator and toast error message
if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false);
Toast.makeText(getContext(), "Failed to load appointments: " + resource.message, Toast.LENGTH_SHORT).show();
Log.e("AppointmentFragment", "Error loading appointments: " + resource.message);
break;
}
});
}
// Load Pets
private void loadPets() {
petApi.getAllPets(0,100).enqueue(new Callback<PageResponse<PetDTO>>() {
@Override
public void onResponse(Call<PageResponse<PetDTO>> call, Response<PageResponse<PetDTO>> response) {
if (response.isSuccessful() && response.body() !=null) {
petList.clear();
petList.addAll(response.body().getContent());
}
}
@Override
public void onFailure(Call<PageResponse<PetDTO>> call, Throwable t) {
Log.e("AppointmentFragment", "Pet load error:" + t.getMessage());
petViewModel.getAllPets(0, 100).observe(getViewLifecycleOwner(), resource -> {
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
petList.clear();
petList.addAll(resource.data.getContent());
}
});
}
// Load Services
private void loadServices() {
serviceApi.getAllServices(0,100).enqueue(new Callback<PageResponse<ServiceDTO>>() {
@Override
public void onResponse(Call<PageResponse<ServiceDTO>> call, Response<PageResponse<ServiceDTO>> response) {
if (response.isSuccessful() && response.body() != null) {
serviceList.clear();
serviceList.addAll(response.body().getContent());
}
}
@Override
public void onFailure(Call<PageResponse<ServiceDTO>> call, Throwable t) {
Log.e("AppointmentFragmnet", "Service load error: " + t.getMessage());
serviceViewModel.getAllServices(0, 100).observe(getViewLifecycleOwner(), resource -> {
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
serviceList.clear();
serviceList.addAll(resource.data.getContent());
}
});
}

View File

@@ -20,6 +20,7 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -28,23 +29,16 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.example.petstoremobile.R;
import com.example.petstoremobile.adapters.BlackTextArrayAdapter;
import com.example.petstoremobile.adapters.InventoryAdapter;
import com.example.petstoremobile.api.CategoryApi;
import com.example.petstoremobile.api.InventoryApi;
import com.example.petstoremobile.dtos.BulkDeleteRequest;
import com.example.petstoremobile.dtos.CategoryDTO;
import com.example.petstoremobile.dtos.InventoryDTO;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.fragments.ListFragment;
import com.example.petstoremobile.viewmodels.InventoryViewModel;
import com.example.petstoremobile.utils.Resource;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
@AndroidEntryPoint
public class InventoryFragment extends Fragment implements InventoryAdapter.OnInventoryClickListener {
@@ -55,8 +49,7 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
private final List<InventoryDTO> inventoryList = new ArrayList<>();
private final List<CategoryDTO> categoryList = new ArrayList<>();
private InventoryAdapter adapter;
@Inject InventoryApi inventoryApi;
@Inject CategoryApi categoryApi;
private InventoryViewModel viewModel;
private SwipeRefreshLayout swipeRefreshLayout;
private EditText etSearch;
@@ -82,7 +75,13 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
private boolean spinnerReady = false;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(InventoryViewModel.class);
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_inventory, container, false);
@@ -117,21 +116,13 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
// Categories
private void loadCategories() {
categoryApi.getAllCategories(0, 100).enqueue(new Callback<PageResponse<CategoryDTO>>() {
@Override
public void onResponse(Call<PageResponse<CategoryDTO>> call,
Response<PageResponse<CategoryDTO>> response) {
if (response.isSuccessful() && response.body() != null) {
categoryList.clear();
categoryList.addAll(response.body().getContent());
setupCategorySpinner();
}
}
@Override
public void onFailure(Call<PageResponse<CategoryDTO>> call, Throwable t) {
Log.e(TAG, "Failed to load categories", t);
// Still setup spinner with just "All"
viewModel.getAllCategories(0, 100).observe(getViewLifecycleOwner(), resource -> {
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
categoryList.clear();
categoryList.addAll(resource.data.getContent());
setupCategorySpinner();
} else if (resource != null && resource.status == Resource.Status.ERROR) {
Log.e(TAG, "Failed to load categories: " + resource.message);
setupCategorySpinner();
}
});
@@ -145,33 +136,35 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
categoryNames.add(c.getCategoryName());
}
BlackTextArrayAdapter<String> spinnerAdapter = new BlackTextArrayAdapter<>(
requireContext(),
android.R.layout.simple_spinner_item,
categoryNames);
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinnerCategory.setAdapter(spinnerAdapter);
if (getContext() != null) {
BlackTextArrayAdapter<String> spinnerAdapter = new BlackTextArrayAdapter<>(
requireContext(),
android.R.layout.simple_spinner_item,
categoryNames);
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinnerCategory.setAdapter(spinnerAdapter);
spinnerCategory.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (!spinnerReady) {
// Skip the first automatic trigger on setup
spinnerReady = true;
return;
spinnerCategory.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (!spinnerReady) {
// Skip the first automatic trigger on setup
spinnerReady = true;
return;
}
if (position == 0) {
selectedCategory = null; // "All Categories"
} else {
selectedCategory = categoryList.get(position - 1).getCategoryName();
}
loadInventory(true);
}
if (position == 0) {
selectedCategory = null; // "All Categories"
} else {
selectedCategory = categoryList.get(position - 1).getCategoryName();
}
loadInventory(true);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
}
// Search
@@ -209,7 +202,7 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
rv.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
if (dy <= 0)
return;
LinearLayoutManager lm = (LinearLayoutManager) rv.getLayoutManager();
@@ -230,11 +223,10 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
swipeRefreshLayout.setOnRefreshListener(() -> loadInventory(true));
}
// Load inventory
// Helper function to get a list of all inventory items from the backend
private void loadInventory(boolean reset) {
if (isLoading)
return;
isLoading = true;
if (reset) {
currentPage = 0;
@@ -244,39 +236,38 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
// Build query: combine search text + selected category
String q = buildQuery();
inventoryApi.getAllInventory(q, currentPage, PAGE_SIZE, "inventoryId,asc")
.enqueue(new Callback<PageResponse<InventoryDTO>>() {
@Override
public void onResponse(Call<PageResponse<InventoryDTO>> call,
Response<PageResponse<InventoryDTO>> response) {
isLoading = false;
if (swipeRefreshLayout != null)
swipeRefreshLayout.setRefreshing(false);
//Load all inventory items from the backend using viewModel
viewModel.getAllInventory(q, currentPage, PAGE_SIZE, "inventoryId,asc").observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return;
if (response.isSuccessful() && response.body() != null) {
PageResponse<InventoryDTO> page = response.body();
if (reset)
inventoryList.clear();
inventoryList.addAll(page.getContent());
adapter.notifyDataSetChanged();
isLastPage = page.isLast();
if (!isLastPage)
currentPage++;
} else {
Log.e(TAG, "Error " + response.code());
Toast.makeText(getContext(), "Failed to load inventory", Toast.LENGTH_SHORT).show();
}
// Check the status to see if the resource is loaded and display the data
switch (resource.status) {
case LOADING:
// Show loading indicator
isLoading = true;
if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(true);
break;
case SUCCESS:
// Hide loading indicator and display data
isLoading = false;
if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false);
if (resource.data != null) {
if (reset) inventoryList.clear();
inventoryList.addAll(resource.data.getContent());
adapter.notifyDataSetChanged();
isLastPage = resource.data.isLast();
if (!isLastPage) currentPage++;
}
@Override
public void onFailure(Call<PageResponse<InventoryDTO>> call, Throwable t) {
isLoading = false;
if (swipeRefreshLayout != null)
swipeRefreshLayout.setRefreshing(false);
Log.e(TAG, "Network error", t);
Toast.makeText(getContext(), "Network error: " + t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
break;
case ERROR:
// Hide loading indicator and toast error message
isLoading = false;
if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false);
Log.e(TAG, "Error: " + resource.message);
Toast.makeText(getContext(), "Failed to load inventory: " + resource.message, Toast.LENGTH_SHORT).show();
break;
}
});
}
// Combines search text and category into one query string for ?q=
@@ -308,25 +299,18 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
}
private void bulkDelete(List<Long> ids) {
inventoryApi.bulkDeleteInventory(new BulkDeleteRequest(ids))
.enqueue(new Callback<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> response) {
if (response.isSuccessful()) {
adapter.clearSelection();
hideBulkDeleteBar();
loadInventory(true);
Toast.makeText(getContext(), ids.size() + " item(s) deleted", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getContext(), "Delete failed", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<Void> call, Throwable t) {
Toast.makeText(getContext(), "Network error", Toast.LENGTH_SHORT).show();
}
});
viewModel.bulkDeleteInventory(ids).observe(getViewLifecycleOwner(), resource -> {
if (resource != null && resource.status != Resource.Status.LOADING) {
if (resource.status == Resource.Status.SUCCESS) {
adapter.clearSelection();
hideBulkDeleteBar();
loadInventory(true);
Toast.makeText(getContext(), ids.size() + " item(s) deleted", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getContext(), "Delete failed: " + resource.message, Toast.LENGTH_SHORT).show();
}
}
});
}
private void hideBulkDeleteBar() {
@@ -374,4 +358,4 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
hideBulkDeleteBar();
}
}
}
}

View File

@@ -5,6 +5,7 @@ import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -25,10 +26,10 @@ import android.widget.Toast;
import com.example.petstoremobile.R;
import com.example.petstoremobile.adapters.BlackTextArrayAdapter;
import com.example.petstoremobile.adapters.PetAdapter;
import com.example.petstoremobile.api.PetApi;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.dtos.PetDTO;
import com.example.petstoremobile.fragments.ListFragment;
import com.example.petstoremobile.viewmodels.PetViewModel;
import com.example.petstoremobile.utils.Resource;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.ArrayList;
@@ -38,9 +39,6 @@ import javax.inject.Inject;
import javax.inject.Named;
import dagger.hilt.android.AndroidEntryPoint;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
@AndroidEntryPoint
public class PetFragment extends Fragment implements PetAdapter.OnPetClickListener {
@@ -48,13 +46,19 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
private List<PetDTO> filteredList = new ArrayList<>();
private ImageButton hamburger;
private PetAdapter adapter;
@Inject PetApi api;
private PetViewModel viewModel;
@Inject @Named("baseUrl") String baseUrl;
private SwipeRefreshLayout swipeRefreshLayout;
private EditText etSearch;
private Spinner spinnerStatus;
//load pet view
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(PetViewModel.class);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
@@ -67,12 +71,9 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
setupStatusFilter(view);
setupSwipeRefresh(view);
//Add button to opens the add dialog
FloatingActionButton fabAddPet = view.findViewById(R.id.fabAddPet);
fabAddPet.setOnClickListener(v -> openPetDetails(-1));
//Make the hamburger button open the drawer from listFragment
hamburger.setOnClickListener(v -> {
Fragment parent = getParentFragment();
if (parent != null) {
@@ -103,7 +104,6 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
});
}
//Setup the status filter spinner
private void setupStatusFilter(View view) {
spinnerStatus = view.findViewById(R.id.spinnerStatus);
String[] statuses = {"All Statuses", "Available", "Adopted"};
@@ -122,7 +122,6 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
});
}
// Helper function to filter pets based on search and status filter
private void filterPets() {
String query = etSearch.getText().toString().toLowerCase();
String selectedStatus = spinnerStatus.getSelectedItem().toString();
@@ -146,14 +145,10 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
private void setupSwipeRefresh(View view) {
swipeRefreshLayout = view.findViewById(R.id.swipeRefreshPet);
swipeRefreshLayout.setOnRefreshListener(() -> {
loadPetData();
});
swipeRefreshLayout.setOnRefreshListener(this::loadPetData);
}
//Open pet profile
private void openPetProfile(int position) {
//Make a bundle to pass data to the profile fragment
Bundle args = new Bundle();
PetDTO pet = filteredList.get(position);
args.putInt("petId", pet.getPetId().intValue());
@@ -172,12 +167,10 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
NavHostFragment.findNavController(this).navigate(R.id.nav_pet_profile, args);
}
//Open the pet detail view for adding
private void openPetDetails(int position) {
NavHostFragment.findNavController(this).navigate(R.id.nav_pet_detail);
}
// Called by PetAdapter when a row is clicked to open the details view
@Override
public void onPetClick(int position) {
openPetProfile(position);
@@ -185,38 +178,35 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
// Helper function to get a list of all pets from the backend
private void loadPetData() {
if (swipeRefreshLayout != null) {
swipeRefreshLayout.setRefreshing(true);
}
api.getAllPets(0, 100).enqueue(new Callback<PageResponse<PetDTO>>() {
@Override
public void onResponse(Call<PageResponse<PetDTO>> call, Response<PageResponse<PetDTO>> response) {
if (swipeRefreshLayout != null) {
swipeRefreshLayout.setRefreshing(false);
}
if (response.isSuccessful() && response.body() != null) {
petList.clear();
petList.addAll(response.body().getContent());
filterPets();
} else {
Log.e("onResponse: ", response.message());
}
}
@Override
public void onFailure(Call<PageResponse<PetDTO>> call, Throwable t) {
if (swipeRefreshLayout != null) {
swipeRefreshLayout.setRefreshing(false);
}
Toast.makeText(getContext(),
"Failed to load pets", Toast.LENGTH_SHORT).show();
Log.e("onFailure: ", t.getMessage());
//Load all pets from the backend using viewModel
viewModel.getAllPets(0, 100).observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return;
// Check the status to see if the resource is loaded and display the data
switch (resource.status) {
case LOADING:
// Show loading indicator
if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(true);
break;
case SUCCESS:
// Hide loading indicator and display data
if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false);
if (resource.data != null) {
petList.clear();
petList.addAll(resource.data.getContent());
filterPets();
}
break;
case ERROR:
// Hide loading indicator and toast error message
if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false);
Toast.makeText(getContext(), "Failed to load pets: " + resource.message, Toast.LENGTH_SHORT).show();
Log.e("PetFragment", "Error loading pets: " + resource.message);
break;
}
});
}
//set up the recyclerview and adapter
private void setupRecyclerView(View view) {
RecyclerView recyclerView = view.findViewById(R.id.recyclerViewPets);
adapter = new PetAdapter(filteredList, this);

View File

@@ -5,18 +5,21 @@ import android.text.*;
import android.util.Log;
import android.view.*;
import android.widget.*;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
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.ProductAdapter;
import com.example.petstoremobile.api.ProductApi;
import com.example.petstoremobile.api.auth.TokenManager;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.dtos.ProductDTO;
import com.example.petstoremobile.fragments.ListFragment;
import com.example.petstoremobile.viewmodels.ProductViewModel;
import com.example.petstoremobile.utils.Resource;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.*;
@@ -24,7 +27,6 @@ import javax.inject.Inject;
import javax.inject.Named;
import dagger.hilt.android.AndroidEntryPoint;
import retrofit2.*;
@AndroidEntryPoint
public class ProductFragment extends Fragment implements ProductAdapter.OnProductClickListener {
@@ -34,13 +36,19 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc
private ProductAdapter adapter;
private SwipeRefreshLayout swipeRefresh;
private EditText etSearch;
private ProductViewModel viewModel;
@Inject ProductApi api;
@Inject @Named("baseUrl") String baseUrl;
@Inject TokenManager tokenManager;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(ProductViewModel.class);
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_product, container, false);
@@ -109,27 +117,35 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc
adapter.notifyDataSetChanged();
}
// Helper function to get a list of all products from the backend
private void loadProducts() {
if (swipeRefresh != null) swipeRefresh.setRefreshing(true);
api.getAllProducts(null, 0, 100)
.enqueue(new Callback<PageResponse<ProductDTO>>() {
public void onResponse(Call<PageResponse<ProductDTO>> c,
Response<PageResponse<ProductDTO>> r) {
if (swipeRefresh != null) swipeRefresh.setRefreshing(false);
if (r.isSuccessful() && r.body() != null) {
productList.clear();
productList.addAll(r.body().getContent());
filter();
} else {
Toast.makeText(getContext(), "Failed to load products",
Toast.LENGTH_SHORT).show();
}
//Load all products from the backend using viewModel
viewModel.getAllProducts(null, 0, 100).observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return;
// Check the status to see if the resource is loaded and display the data
switch (resource.status) {
case LOADING:
// Show loading indicator
if (swipeRefresh != null) swipeRefresh.setRefreshing(true);
break;
case SUCCESS:
// Hide loading indicator and display data
if (swipeRefresh != null) swipeRefresh.setRefreshing(false);
if (resource.data != null) {
productList.clear();
productList.addAll(resource.data.getContent());
filter();
}
public void onFailure(Call<PageResponse<ProductDTO>> c, Throwable t) {
if (swipeRefresh != null) swipeRefresh.setRefreshing(false);
Log.e("ProductFragment", t.getMessage());
}
});
break;
case ERROR:
// Hide loading indicator and toast error message
if (swipeRefresh != null) swipeRefresh.setRefreshing(false);
Toast.makeText(getContext(), "Failed to load products: " + resource.message, Toast.LENGTH_SHORT).show();
Log.e("ProductFragment", "Error loading products: " + resource.message);
break;
}
});
}
private void openDetail(int position) {

View File

@@ -5,25 +5,24 @@ import android.text.*;
import android.util.Log;
import android.view.*;
import android.widget.*;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
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.ProductSupplierAdapter;
import com.example.petstoremobile.api.ProductSupplierApi;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.dtos.ProductSupplierDTO;
import com.example.petstoremobile.fragments.ListFragment;
import com.example.petstoremobile.fragments.listfragments.detailfragments.ProductSupplierDetailFragment;
import com.example.petstoremobile.viewmodels.ProductSupplierViewModel;
import com.example.petstoremobile.utils.Resource;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.*;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
import retrofit2.*;
@AndroidEntryPoint
public class ProductSupplierFragment extends Fragment
@@ -34,11 +33,16 @@ public class ProductSupplierFragment extends Fragment
private ProductSupplierAdapter adapter;
private SwipeRefreshLayout swipeRefresh;
private EditText etSearch;
@Inject ProductSupplierApi api;
private ProductSupplierViewModel viewModel;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(ProductSupplierViewModel.class);
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_product_supplier, container, false);
@@ -103,27 +107,35 @@ public class ProductSupplierFragment extends Fragment
adapter.notifyDataSetChanged();
}
// Helper function to get a list of all product suppliers from the backend
private void loadData() {
if (swipeRefresh != null) swipeRefresh.setRefreshing(true);
api.getAllProductSuppliers(0, 100)
.enqueue(new Callback<PageResponse<ProductSupplierDTO>>() {
public void onResponse(Call<PageResponse<ProductSupplierDTO>> c,
Response<PageResponse<ProductSupplierDTO>> r) {
if (swipeRefresh != null) swipeRefresh.setRefreshing(false);
if (r.isSuccessful() && r.body() != null) {
psList.clear();
psList.addAll(r.body().getContent());
filter(etSearch != null ? etSearch.getText().toString() : "");
} else {
Toast.makeText(getContext(), "Failed to load",
Toast.LENGTH_SHORT).show();
}
//Load all product suppliers from the backend using viewModel
viewModel.getAllProductSuppliers(0, 100).observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return;
// Check the status to see if the resource is loaded and display the data
switch (resource.status) {
case LOADING:
// Show loading indicator
if (swipeRefresh != null) swipeRefresh.setRefreshing(true);
break;
case SUCCESS:
// Hide loading indicator and display data
if (swipeRefresh != null) swipeRefresh.setRefreshing(false);
if (resource.data != null) {
psList.clear();
psList.addAll(resource.data.getContent());
filter(etSearch != null ? etSearch.getText().toString() : "");
}
public void onFailure(Call<PageResponse<ProductSupplierDTO>> c, Throwable t) {
if (swipeRefresh != null) swipeRefresh.setRefreshing(false);
Log.e("PSFragment", t.getMessage());
}
});
break;
case ERROR:
// Hide loading indicator and toast error message
if (swipeRefresh != null) swipeRefresh.setRefreshing(false);
Toast.makeText(getContext(), "Failed to load: " + resource.message, Toast.LENGTH_SHORT).show();
Log.e("PSFragment", "Error loading: " + resource.message);
break;
}
});
}
private void openDetail(int position) {
@@ -141,4 +153,4 @@ public class ProductSupplierFragment extends Fragment
@Override
public void onProductSupplierClick(int position) { openDetail(position); }
}
}

View File

@@ -5,23 +5,23 @@ import android.text.*;
import android.util.Log;
import android.view.*;
import android.widget.*;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
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.PurchaseOrderAdapter;
import com.example.petstoremobile.api.PurchaseOrderApi;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.dtos.PurchaseOrderDTO;
import com.example.petstoremobile.fragments.ListFragment;
import com.example.petstoremobile.viewmodels.PurchaseOrderViewModel;
import com.example.petstoremobile.utils.Resource;
import java.util.*;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
import retrofit2.*;
@AndroidEntryPoint
public class PurchaseOrderFragment extends Fragment
@@ -32,8 +32,13 @@ public class PurchaseOrderFragment extends Fragment
private PurchaseOrderAdapter adapter;
private SwipeRefreshLayout swipeRefresh;
private EditText etSearch;
private PurchaseOrderViewModel viewModel;
@Inject PurchaseOrderApi api;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(PurchaseOrderViewModel.class);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
@@ -75,7 +80,7 @@ public class PurchaseOrderFragment extends Fragment
public void afterTextChanged(Editable s) {
}
public void onTextChanged(CharSequence s, int a, int b, int c) {
public void onTextChanged(CharSequence s, int start, int before, int count) {
filter(s.toString());
}
});
@@ -102,31 +107,35 @@ public class PurchaseOrderFragment extends Fragment
adapter.notifyDataSetChanged();
}
// Helper function to get a list of all purchase orders from the backend
private void loadData() {
if (swipeRefresh != null)
swipeRefresh.setRefreshing(true);
api.getAllPurchaseOrders(0, 100)
.enqueue(new Callback<PageResponse<PurchaseOrderDTO>>() {
public void onResponse(Call<PageResponse<PurchaseOrderDTO>> c,
Response<PageResponse<PurchaseOrderDTO>> r) {
if (swipeRefresh != null)
swipeRefresh.setRefreshing(false);
if (r.isSuccessful() && r.body() != null) {
poList.clear();
poList.addAll(r.body().getContent());
filter(etSearch != null ? etSearch.getText().toString() : "");
} else {
Toast.makeText(getContext(), "Failed to load purchase orders",
Toast.LENGTH_SHORT).show();
}
}
//Load all purchase orders from the backend using viewModel
viewModel.getAllPurchaseOrders(0, 100).observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return;
public void onFailure(Call<PageResponse<PurchaseOrderDTO>> c, Throwable t) {
if (swipeRefresh != null)
swipeRefresh.setRefreshing(false);
Log.e("POFragment", t.getMessage());
// Check the status to see if the resource is loaded and display the data
switch (resource.status) {
case LOADING:
// Show loading indicator
if (swipeRefresh != null) swipeRefresh.setRefreshing(true);
break;
case SUCCESS:
// Hide loading indicator and display data
if (swipeRefresh != null) swipeRefresh.setRefreshing(false);
if (resource.data != null) {
poList.clear();
poList.addAll(resource.data.getContent());
filter(etSearch != null ? etSearch.getText().toString() : "");
}
});
break;
case ERROR:
// Hide loading indicator and toast error message
if (swipeRefresh != null) swipeRefresh.setRefreshing(false);
Toast.makeText(getContext(), "Failed to load purchase orders: " + resource.message, Toast.LENGTH_SHORT).show();
Log.e("POFragment", "Error loading purchase orders: " + resource.message);
break;
}
});
}
private void openDetail(int position) {
@@ -143,4 +152,4 @@ public class PurchaseOrderFragment extends Fragment
public void onPurchaseOrderClick(int position) {
openDetail(position);
}
}
}

View File

@@ -1,6 +1,8 @@
package com.example.petstoremobile.fragments.listfragments;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.LinearLayoutManager;

View File

@@ -5,6 +5,7 @@ import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -22,21 +23,16 @@ import android.widget.Toast;
import com.example.petstoremobile.R;
import com.example.petstoremobile.adapters.ServiceAdapter;
import com.example.petstoremobile.api.ServiceApi;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.dtos.ServiceDTO;
import com.example.petstoremobile.fragments.ListFragment;
import com.example.petstoremobile.viewmodels.ServiceViewModel;
import com.example.petstoremobile.utils.Resource;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
@AndroidEntryPoint
public class ServiceFragment extends Fragment implements ServiceAdapter.OnServiceClickListener {
@@ -45,13 +41,19 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
private List<ServiceDTO> filteredList = new ArrayList<>();
private ServiceAdapter adapter;
private ImageButton hamburger;
@Inject ServiceApi api;
private ServiceViewModel viewModel;
private SwipeRefreshLayout swipeRefreshLayout;
private EditText etSearch;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(ServiceViewModel.class);
}
//load service view
@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_service, container, false);
@@ -98,8 +100,8 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
} else {
String lower = query.toLowerCase();
for (ServiceDTO s : serviceList) {
if (s.getServiceName().toLowerCase().contains(lower)
|| s.getServiceDesc().toLowerCase().contains(lower)) {
if ((s.getServiceName() != null && s.getServiceName().toLowerCase().contains(lower))
|| (s.getServiceDesc() != null && s.getServiceDesc().toLowerCase().contains(lower))) {
filteredList.add(s);
}
}
@@ -109,9 +111,7 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
private void setupSwipeRefresh(View view) {
swipeRefreshLayout = view.findViewById(R.id.swipeRefreshService);
swipeRefreshLayout.setOnRefreshListener(() -> {
loadServiceData();
});
swipeRefreshLayout.setOnRefreshListener(this::loadServiceData);
}
//Open the service detail view depending on the mode
@@ -141,35 +141,33 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
// Helper function to get a list of all services from the backend
private void loadServiceData() {
if (swipeRefreshLayout != null) {
swipeRefreshLayout.setRefreshing(true);
}
api.getAllServices(0, 100).enqueue(new Callback<PageResponse<ServiceDTO>>() {
@Override
public void onResponse(Call<PageResponse<ServiceDTO>> call, Response<PageResponse<ServiceDTO>> response) {
if (swipeRefreshLayout != null) {
swipeRefreshLayout.setRefreshing(false);
}
if (response.isSuccessful() && response.body() != null) {
serviceList.clear();
serviceList.addAll(response.body().getContent());
filterServices(etSearch.getText().toString());
//Load all services from the backend using viewModel
viewModel.getAllServices(0, 100).observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return;
} else {
Log.e("onResponse: ", response.message());
}
}
@Override
public void onFailure(Call<PageResponse<ServiceDTO>> call, Throwable t) {
if (swipeRefreshLayout != null) {
swipeRefreshLayout.setRefreshing(false);
}
if (getContext() != null) {
Toast.makeText(getContext(),
"Failed to load services", Toast.LENGTH_SHORT).show();
}
Log.e("onFailure: ", t.getMessage());
// Check the status to see if the resource is loaded and display the data
switch (resource.status) {
case LOADING:
// Show loading indicator
if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(true);
break;
case SUCCESS:
// Hide loading indicator and display data
if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false);
if (resource.data != null) {
serviceList.clear();
serviceList.addAll(resource.data.getContent());
filterServices(etSearch != null ? etSearch.getText().toString() : "");
}
break;
case ERROR:
// Hide loading indicator and toast error message
if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false);
if (getContext() != null) {
Toast.makeText(getContext(), "Failed to load services: " + resource.message, Toast.LENGTH_SHORT).show();
}
Log.e("ServiceFragment", "Error loading services: " + resource.message);
break;
}
});
}

View File

@@ -5,6 +5,7 @@ import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -22,21 +23,16 @@ import android.widget.Toast;
import com.example.petstoremobile.R;
import com.example.petstoremobile.adapters.SupplierAdapter;
import com.example.petstoremobile.api.SupplierApi;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.dtos.SupplierDTO;
import com.example.petstoremobile.fragments.ListFragment;
import com.example.petstoremobile.viewmodels.SupplierViewModel;
import com.example.petstoremobile.utils.Resource;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
@AndroidEntryPoint
public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupplierClickListener {
@@ -45,13 +41,19 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp
private List<SupplierDTO> filteredList = new ArrayList<>();
private SupplierAdapter adapter;
private ImageButton hamburger;
@Inject SupplierApi api;
private SupplierViewModel viewModel;
private SwipeRefreshLayout swipeRefreshLayout;
private EditText etSearch;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(SupplierViewModel.class);
}
//load supplier view
@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_supplier, container, false);
@@ -98,9 +100,9 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp
} else {
String lower = query.toLowerCase();
for (SupplierDTO s : supplierList) {
if (s.getSupCompany().toLowerCase().contains(lower)
|| s.getSupContactFirstName().toLowerCase().contains(lower)
|| s.getSupContactLastName().toLowerCase().contains(lower)) {
if ((s.getSupCompany() != null && s.getSupCompany().toLowerCase().contains(lower))
|| (s.getSupContactFirstName() != null && s.getSupContactFirstName().toLowerCase().contains(lower))
|| (s.getSupContactLastName() != null && s.getSupContactLastName().toLowerCase().contains(lower))) {
filteredList.add(s);
}
}
@@ -110,9 +112,7 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp
private void setupSwipeRefresh(View view) {
swipeRefreshLayout = view.findViewById(R.id.swipeRefreshSupplier);
swipeRefreshLayout.setOnRefreshListener(() -> {
loadSupplierData();
});
swipeRefreshLayout.setOnRefreshListener(this::loadSupplierData);
}
//Open the supplier detail view depending on the mode
@@ -144,35 +144,33 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp
// Helper function to get a list of all suppliers from the backend
private void loadSupplierData() {
if (swipeRefreshLayout != null) {
swipeRefreshLayout.setRefreshing(true);
}
api.getAllSuppliers(0, 100).enqueue(new Callback<PageResponse<SupplierDTO>>() {
@Override
public void onResponse(Call<PageResponse<SupplierDTO>> call, Response<PageResponse<SupplierDTO>> response) {
if (swipeRefreshLayout != null) {
swipeRefreshLayout.setRefreshing(false);
}
if (response.isSuccessful() && response.body() != null) {
supplierList.clear();
supplierList.addAll(response.body().getContent());
filterSuppliers(etSearch.getText().toString());
//Load all suppliers from the backend using viewModel
viewModel.getAllSuppliers(0, 100).observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return;
} else {
Log.e("onResponse: ", response.message());
}
}
@Override
public void onFailure(Call<PageResponse<SupplierDTO>> call, Throwable t) {
if (swipeRefreshLayout != null) {
swipeRefreshLayout.setRefreshing(false);
}
if (getContext() != null) {
Toast.makeText(getContext(),
"Failed to load suppliers", Toast.LENGTH_SHORT).show();
}
Log.e("onFailure: ", t.getMessage());
// Check the status to see if the resource is loaded and display the data
switch (resource.status) {
case LOADING:
// Show loading indicator
if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(true);
break;
case SUCCESS:
// Hide loading indicator and display data
if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false);
if (resource.data != null) {
supplierList.clear();
supplierList.addAll(resource.data.getContent());
filterSuppliers(etSearch.getText().toString());
}
break;
case ERROR:
// Hide loading indicator and toast error message
if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false);
if (getContext() != null) {
Toast.makeText(getContext(), "Failed to load suppliers: " + resource.message, Toast.LENGTH_SHORT).show();
}
Log.e("SupplierFragment", "Error loading suppliers: " + resource.message);
break;
}
});
}
@@ -184,4 +182,4 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(adapter);
}
}
}

View File

@@ -13,10 +13,12 @@ import android.widget.*;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.fragment.NavHostFragment;
import com.bumptech.glide.Glide;
@@ -28,9 +30,11 @@ import com.example.petstoremobile.adapters.BlackTextArrayAdapter;
import com.example.petstoremobile.api.*;
import com.example.petstoremobile.api.auth.TokenManager;
import com.example.petstoremobile.dtos.*;
import com.example.petstoremobile.viewmodels.ProductViewModel;
import com.example.petstoremobile.utils.Resource;
import com.example.petstoremobile.utils.FileUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.*;
@@ -41,7 +45,6 @@ import dagger.hilt.android.AndroidEntryPoint;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.*;
@AndroidEntryPoint
public class ProductDetailFragment extends Fragment {
@@ -61,9 +64,8 @@ public class ProductDetailFragment extends Fragment {
private List<CategoryDTO> categoryList = new ArrayList<>();
private Uri photoUri;
private ProductViewModel viewModel;
@Inject ProductApi productApi;
@Inject CategoryApi categoryApi;
@Inject @Named("baseUrl") String baseUrl;
@Inject TokenManager tokenManager;
@@ -74,12 +76,13 @@ public class ProductDetailFragment extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(ProductViewModel.class);
galleryLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) {
Uri selectedImage = result.getData().getData();
// Update image view locally
Glide.with(this).load(selectedImage).into(ivProductImage);
photoUri = selectedImage;
hasImage = true;
@@ -92,7 +95,6 @@ public class ProductDetailFragment extends Fragment {
new ActivityResultContracts.TakePicture(),
success -> {
if (success) {
// Update image view locally
Glide.with(this).load(photoUri).into(ivProductImage);
hasImage = true;
isImageChanged = true;
@@ -185,19 +187,12 @@ public class ProductDetailFragment extends Fragment {
// Helper function to load categories from the backend for the spinner
private void loadCategories() {
categoryApi.getAllCategories(0, 100)
.enqueue(new Callback<PageResponse<CategoryDTO>>() {
public void onResponse(Call<PageResponse<CategoryDTO>> c,
Response<PageResponse<CategoryDTO>> r) {
if (r.isSuccessful() && r.body() != null) {
categoryList = r.body().getContent();
populateCategorySpinner();
}
}
public void onFailure(Call<PageResponse<CategoryDTO>> c, Throwable t) {
Log.e("ProductDetail", "Category load failed: " + t.getMessage());
}
});
viewModel.getAllCategories(0, 100).observe(getViewLifecycleOwner(), resource -> {
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
categoryList = resource.data.getContent();
populateCategorySpinner();
}
});
}
// Helper function to populate the category spinner
@@ -265,24 +260,19 @@ public class ProductDetailFragment extends Fragment {
// updating/adding photo, removing photo or no change
private void performPendingImageActions(String successMsg) {
if (isImageRemoved) {
//if the image is removed then delete the image
productApi.deleteProductImage(prodId).enqueue(new Callback<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> response) {
Toast.makeText(getContext(), successMsg, Toast.LENGTH_SHORT).show();
navigateBack();
}
@Override
public void onFailure(Call<Void> call, Throwable t) {
Toast.makeText(getContext(), successMsg + " (but image removal failed)", Toast.LENGTH_SHORT).show();
viewModel.deleteProductImage(prodId).observe(getViewLifecycleOwner(), resource -> {
if (resource != null && resource.status != Resource.Status.LOADING) {
if (resource.status == Resource.Status.SUCCESS) {
Toast.makeText(getContext(), successMsg, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getContext(), successMsg + " (but image removal failed)", Toast.LENGTH_SHORT).show();
}
navigateBack();
}
});
} else if (isImageChanged && photoUri != null) {
//if the image is changed then upload it
uploadProductImageAndNavigate(photoUri, successMsg);
} else {
//if no changes then navigate back
Toast.makeText(getContext(), successMsg, Toast.LENGTH_SHORT).show();
navigateBack();
}
@@ -291,61 +281,28 @@ public class ProductDetailFragment extends Fragment {
// Helper function to upload the product image by calling the backend
// and then navigate back to the previous screen
private void uploadProductImageAndNavigate(Uri uri, String successMsg) {
try {
File file = getFileFromUri(uri);
if (file == null) {
Toast.makeText(getContext(), successMsg, Toast.LENGTH_SHORT).show();
navigateBack();
return;
}
RequestBody requestFile = RequestBody.create(file, MediaType.parse(requireContext().getContentResolver().getType(uri)));
MultipartBody.Part body = MultipartBody.Part.createFormData("image", file.getName(), requestFile);
productApi.uploadProductImage(prodId, body)
.enqueue(new Callback<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> response) {
if (response.isSuccessful()) {
Toast.makeText(getContext(), successMsg, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getContext(), successMsg + " (but image upload failed)", Toast.LENGTH_SHORT).show();
}
navigateBack();
}
@Override
public void onFailure(Call<Void> call, Throwable t) {
Toast.makeText(getContext(), successMsg + " (network error during upload)", Toast.LENGTH_SHORT).show();
navigateBack();
}
});
} catch (Exception e) {
Log.e("ProductDetail", "Error uploading image", e);
File file = FileUtils.getFileFromUri(requireContext(), uri);
if (file == null) {
Toast.makeText(getContext(), successMsg, Toast.LENGTH_SHORT).show();
navigateBack();
return;
}
}
// Helper function to get the File from the Uri
private File getFileFromUri(Uri uri) {
try {
InputStream inputStream = requireContext().getContentResolver().openInputStream(uri);
File tempFile = new File(requireContext().getCacheDir(), "upload_product_image.jpg");
FileOutputStream outputStream = new FileOutputStream(tempFile);
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
RequestBody requestFile = RequestBody.create(file, MediaType.parse(requireContext().getContentResolver().getType(uri)));
MultipartBody.Part body = MultipartBody.Part.createFormData("image", file.getName(), requestFile);
viewModel.uploadProductImage(prodId, body).observe(getViewLifecycleOwner(), resource -> {
if (resource != null && resource.status != Resource.Status.LOADING) {
if (resource.status == Resource.Status.SUCCESS) {
Toast.makeText(getContext(), successMsg, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getContext(), successMsg + " (but image upload failed)", Toast.LENGTH_SHORT).show();
}
navigateBack();
}
outputStream.close();
inputStream.close();
return tempFile;
} catch (Exception e) {
return null;
}
});
}
// Function to save the product to the server
private void saveProduct() {
String name = etProductName.getText().toString().trim();
String desc = etProductDesc.getText().toString().trim();
@@ -372,35 +329,25 @@ public class ProductDetailFragment extends Fragment {
ProductDTO dto = new ProductDTO(name, category.getCategoryId(), desc, price);
if (isEditing) {
productApi.updateProduct(prodId, dto).enqueue(new Callback<ProductDTO>() {
@Override
public void onResponse(Call<ProductDTO> call, Response<ProductDTO> response) {
if (response.isSuccessful()) {
viewModel.updateProduct(prodId, dto).observe(getViewLifecycleOwner(), resource -> {
if (resource != null && resource.status != Resource.Status.LOADING) {
if (resource.status == Resource.Status.SUCCESS) {
performPendingImageActions("Updated");
} else {
Toast.makeText(getContext(), "Error " + response.code(), Toast.LENGTH_SHORT).show();
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<ProductDTO> call, Throwable t) {
Toast.makeText(getContext(), "Network error", Toast.LENGTH_SHORT).show();
}
});
} else {
productApi.createProduct(dto).enqueue(new Callback<ProductDTO>() {
@Override
public void onResponse(Call<ProductDTO> call, Response<ProductDTO> response) {
if (response.isSuccessful() && response.body() != null) {
prodId = response.body().getProdId();
viewModel.createProduct(dto).observe(getViewLifecycleOwner(), resource -> {
if (resource != null && resource.status != Resource.Status.LOADING) {
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
prodId = resource.data.getProdId();
performPendingImageActions("Saved");
} else {
Toast.makeText(getContext(), "Error saving", Toast.LENGTH_SHORT).show();
Toast.makeText(getContext(), "Error saving: " + resource.message, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<ProductDTO> call, Throwable t) {
Toast.makeText(getContext(), "Network error", Toast.LENGTH_SHORT).show();
}
});
}
}
@@ -410,20 +357,17 @@ public class ProductDetailFragment extends Fragment {
new AlertDialog.Builder(requireContext())
.setTitle("Delete Product?")
.setPositiveButton("Yes", (d, w) ->
productApi.deleteProduct(prodId)
.enqueue(new Callback<Void>() {
public void onResponse(Call<Void> c, Response<Void> r) {
navigateBack();
}
public void onFailure(Call<Void> c, Throwable t) {
Toast.makeText(getContext(), "Delete failed",
Toast.LENGTH_SHORT).show();
}
}))
viewModel.deleteProduct(prodId).observe(getViewLifecycleOwner(), resource -> {
if (resource != null && resource.status == Resource.Status.SUCCESS) {
navigateBack();
} else if (resource != null && resource.status == Resource.Status.ERROR) {
Toast.makeText(getContext(), "Delete failed: " + resource.message, Toast.LENGTH_SHORT).show();
}
}))
.setNegativeButton("No", null).show();
}
private void navigateBack() {
NavHostFragment.findNavController(this).popBackStack();
}
}
}

View File

@@ -1,79 +0,0 @@
package com.example.petstoremobile.models;
public class Adoption {
private int adoptionId;
private String adopterName;
private String adopterEmail;
private String adopterPhone;
private String petName;
private String adoptionDate;
private String status;
// Constructor
public Adoption(int adoptionId, String adopterName, String adopterEmail, String adopterPhone, String petName, String adoptionDate, String status) {
this.adoptionId = adoptionId;
this.adopterName = adopterName;
this.adopterEmail = adopterEmail;
this.adopterPhone = adopterPhone;
this.petName = petName;
this.adoptionDate = adoptionDate;
this.status = status;
}
// Getters and setters
public int getAdoptionId() {
return adoptionId;
}
public void setAdoptionId(int adoptionId) {
this.adoptionId = adoptionId;
}
public String getAdopterName() {
return adopterName;
}
public void setAdopterName(String adopterName) {
this.adopterName = adopterName;
}
public String getAdopterEmail() {
return adopterEmail;
}
public void setAdopterEmail(String adopterEmail) {
this.adopterEmail = adopterEmail;
}
public String getAdopterPhone() {
return adopterPhone;
}
public void setAdopterPhone(String adopterPhone) {
this.adopterPhone = adopterPhone;
}
public String getPetName() {
return petName;
}
public void setPetName(String petName) {
this.petName = petName;
}
public String getAdoptionDate() {
return adoptionDate;
}
public void setAdoptionDate(String adoptionDate) {
this.adoptionDate = adoptionDate;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}

View File

@@ -1,76 +0,0 @@
package com.example.petstoremobile.models;
public class Appointment {
private int appointmentId;
private String customerName;
private String petName;
private String serviceType;
private String appointmentDate;
private String appointmentTime;
private String status;
// Constructor
public Appointment(int appointmentId, String customerName, String petName, String serviceType, String appointmentDate, String appointmentTime, String status) {
this.appointmentId = appointmentId;
this.customerName = customerName;
this.petName = petName;
this.serviceType = serviceType;
this.appointmentDate = appointmentDate;
this.appointmentTime = appointmentTime;
this.status = status;
}
// Getters and setters
public int getAppointmentId() {
return appointmentId;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public String getPetName() {
return petName;
}
public void setPetName(String petName) {
this.petName = petName;
}
public String getServiceType() {
return serviceType;
}
public void setServiceType(String serviceType) {
this.serviceType = serviceType;
}
public String getAppointmentDate() {
return appointmentDate;
}
public void setAppointmentDate(String appointmentDate) {
this.appointmentDate = appointmentDate;
}
public String getAppointmentTime() {
return appointmentTime;
}
public void setAppointmentTime(String appointmentTime) {
this.appointmentTime = appointmentTime;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}

View File

@@ -1,66 +0,0 @@
package com.example.petstoremobile.models;
public class Inventory {
private int inventoryId;
private String itemName;
private String category;
private int quantity;
private double unitPrice;
private String supplier;
// Constructor
public Inventory(int inventoryId, String itemName, String category, int quantity, double unitPrice, String supplier) {
this.inventoryId = inventoryId;
this.itemName = itemName;
this.category = category;
this.quantity = quantity;
this.unitPrice = unitPrice;
this.supplier = supplier;
}
// Getters and setters
public int getInventoryId() {
return inventoryId;
}
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public double getUnitPrice() {
return unitPrice;
}
public void setUnitPrice(double unitPrice) {
this.unitPrice = unitPrice;
}
public String getSupplier() {
return supplier;
}
public void setSupplier(String supplier) {
this.supplier = supplier;
}
}

View File

@@ -1,66 +0,0 @@
package com.example.petstoremobile.models;
public class Product {
private int productId;
private String productName;
private String productDesc;
private String category;
private double productPrice;
private int stockQuantity;
// Constructor
public Product(int productId, String productName, String productDesc, String category, double productPrice, int stockQuantity) {
this.productId = productId;
this.productName = productName;
this.productDesc = productDesc;
this.category = category;
this.productPrice = productPrice;
this.stockQuantity = stockQuantity;
}
// Getters and setters
public int getProductId() {
return productId;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String getProductDesc() {
return productDesc;
}
public void setProductDesc(String productDesc) {
this.productDesc = productDesc;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public double getProductPrice() {
return productPrice;
}
public void setProductPrice(double productPrice) {
this.productPrice = productPrice;
}
public int getStockQuantity() {
return stockQuantity;
}
public void setStockQuantity(int stockQuantity) {
this.stockQuantity = stockQuantity;
}
}

View File

@@ -1,49 +0,0 @@
package com.example.petstoremobile.models;
public class ProductSupplier {
private int supId;
private int prodId;
private String supCompany;
private String prodName;
private double cost;
public ProductSupplier(int supId, int prodId, String supCompany, String prodName, double cost) {
this.supId = supId;
this.prodId = prodId;
this.supCompany = supCompany;
this.prodName = prodName;
this.cost = cost;
}
public int getSupId() {
return supId;
}
public int getProdId() {
return prodId;
}
public String getSupCompany() {
return supCompany;
}
public String getProdName() {
return prodName;
}
public double getCost() {
return cost;
}
public void setSupCompany(String supCompany) {
this.supCompany = supCompany;
}
public void setProdName(String prodName) {
this.prodName = prodName;
}
public void setCost(double cost) {
this.cost = cost;
}
}

View File

@@ -1,31 +0,0 @@
package com.example.petstoremobile.models;
public class PurchaseOrder {
private int purchaseOrderId;
private String supplierName;
private String orderDate;
private String status;
public PurchaseOrder(int purchaseOrderId, String supplierName, String orderDate, String status) {
this.purchaseOrderId = purchaseOrderId;
this.supplierName = supplierName;
this.orderDate = orderDate;
this.status = status;
}
public int getPurchaseOrderId() {
return purchaseOrderId;
}
public String getSupplierName() {
return supplierName;
}
public String getOrderDate() {
return orderDate;
}
public String getStatus() {
return status;
}
}

View File

@@ -0,0 +1,156 @@
package com.example.petstoremobile.repositories;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.example.petstoremobile.api.AdoptionApi;
import com.example.petstoremobile.dtos.AdoptionDTO;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.utils.Resource;
import javax.inject.Inject;
import javax.inject.Singleton;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
@Singleton
public class AdoptionRepository {
private final AdoptionApi adoptionApi;
@Inject
public AdoptionRepository(AdoptionApi adoptionApi) {
this.adoptionApi = adoptionApi;
}
/**
* Retrieves a paginated list of all adoptions from the API.
*/
public LiveData<Resource<PageResponse<AdoptionDTO>>> getAllAdoptions(int page, int size) {
MutableLiveData<Resource<PageResponse<AdoptionDTO>>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
adoptionApi.getAllAdoptions(page, size).enqueue(new Callback<PageResponse<AdoptionDTO>>() {
@Override
public void onResponse(Call<PageResponse<AdoptionDTO>> call, Response<PageResponse<AdoptionDTO>> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<PageResponse<AdoptionDTO>> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Retrieves a specific adoption record by its ID from the API.
*/
public LiveData<Resource<AdoptionDTO>> getAdoptionById(Long id) {
MutableLiveData<Resource<AdoptionDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
adoptionApi.getAdoptionById(id).enqueue(new Callback<AdoptionDTO>() {
@Override
public void onResponse(Call<AdoptionDTO> call, Response<AdoptionDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<AdoptionDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to create a new adoption record.
*/
public LiveData<Resource<AdoptionDTO>> createAdoption(AdoptionDTO adoption) {
MutableLiveData<Resource<AdoptionDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
adoptionApi.createAdoption(adoption).enqueue(new Callback<AdoptionDTO>() {
@Override
public void onResponse(Call<AdoptionDTO> call, Response<AdoptionDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<AdoptionDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to update an existing adoption record by ID.
*/
public LiveData<Resource<AdoptionDTO>> updateAdoption(Long id, AdoptionDTO adoption) {
MutableLiveData<Resource<AdoptionDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
adoptionApi.updateAdoption(id, adoption).enqueue(new Callback<AdoptionDTO>() {
@Override
public void onResponse(Call<AdoptionDTO> call, Response<AdoptionDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<AdoptionDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to delete a specific adoption record.
*/
public LiveData<Resource<Void>> deleteAdoption(Long id) {
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
adoptionApi.deleteAdoption(id).enqueue(new Callback<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> response) {
if (response.isSuccessful()) {
data.setValue(Resource.success(null));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<Void> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
}

View File

@@ -0,0 +1,156 @@
package com.example.petstoremobile.repositories;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.example.petstoremobile.api.AppointmentApi;
import com.example.petstoremobile.dtos.AppointmentDTO;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.utils.Resource;
import javax.inject.Inject;
import javax.inject.Singleton;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
@Singleton
public class AppointmentRepository {
private final AppointmentApi appointmentApi;
@Inject
public AppointmentRepository(AppointmentApi appointmentApi) {
this.appointmentApi = appointmentApi;
}
/**
* Retrieves a paginated list of all appointments from the API.
*/
public LiveData<Resource<PageResponse<AppointmentDTO>>> getAllAppointments(int page, int size) {
MutableLiveData<Resource<PageResponse<AppointmentDTO>>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
appointmentApi.getAllAppointments(page, size).enqueue(new Callback<PageResponse<AppointmentDTO>>() {
@Override
public void onResponse(Call<PageResponse<AppointmentDTO>> call, Response<PageResponse<AppointmentDTO>> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<PageResponse<AppointmentDTO>> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Retrieves a specific appointment by its ID from the API.
*/
public LiveData<Resource<AppointmentDTO>> getAppointmentById(Long id) {
MutableLiveData<Resource<AppointmentDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
appointmentApi.getAppointmentById(id).enqueue(new Callback<AppointmentDTO>() {
@Override
public void onResponse(Call<AppointmentDTO> call, Response<AppointmentDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<AppointmentDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to create a new appointment record.
*/
public LiveData<Resource<AppointmentDTO>> createAppointment(AppointmentDTO appointment) {
MutableLiveData<Resource<AppointmentDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
appointmentApi.createAppointment(appointment).enqueue(new Callback<AppointmentDTO>() {
@Override
public void onResponse(Call<AppointmentDTO> call, Response<AppointmentDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<AppointmentDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to update an existing appointment record by ID.
*/
public LiveData<Resource<AppointmentDTO>> updateAppointment(Long id, AppointmentDTO appointment) {
MutableLiveData<Resource<AppointmentDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
appointmentApi.updateAppointment(id, appointment).enqueue(new Callback<AppointmentDTO>() {
@Override
public void onResponse(Call<AppointmentDTO> call, Response<AppointmentDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<AppointmentDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to delete a specific appointment record.
*/
public LiveData<Resource<Void>> deleteAppointment(Long id) {
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
appointmentApi.deleteAppointment(id).enqueue(new Callback<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> response) {
if (response.isSuccessful()) {
data.setValue(Resource.success(null));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<Void> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
}

View File

@@ -0,0 +1,174 @@
package com.example.petstoremobile.repositories;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.example.petstoremobile.api.auth.AuthApi;
import com.example.petstoremobile.api.auth.TokenManager;
import com.example.petstoremobile.dtos.AuthDTO;
import com.example.petstoremobile.dtos.UserDTO;
import com.example.petstoremobile.utils.Resource;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import okhttp3.MultipartBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
@Singleton
public class AuthRepository {
private final AuthApi authApi;
private final TokenManager tokenManager;
@Inject
public AuthRepository(AuthApi authApi, TokenManager tokenManager) {
this.authApi = authApi;
this.tokenManager = tokenManager;
}
/**
* Authenticates the user and saves login data (token, username, role) upon success.
*/
public LiveData<Resource<AuthDTO.LoginResponse>> login(AuthDTO.LoginRequest loginRequest) {
MutableLiveData<Resource<AuthDTO.LoginResponse>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
authApi.login(loginRequest).enqueue(new Callback<AuthDTO.LoginResponse>() {
@Override
public void onResponse(Call<AuthDTO.LoginResponse> call, Response<AuthDTO.LoginResponse> response) {
if (response.isSuccessful() && response.body() != null) {
tokenManager.saveLoginData(
response.body().getToken(),
response.body().getUsername(),
response.body().getRole()
);
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Login failed: " + response.message(), null));
}
}
@Override
public void onFailure(Call<AuthDTO.LoginResponse> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Retrieves the current user's profile information from the API.
*/
public LiveData<Resource<UserDTO>> getMe() {
MutableLiveData<Resource<UserDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
authApi.getMe().enqueue(new Callback<UserDTO>() {
@Override
public void onResponse(Call<UserDTO> call, Response<UserDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<UserDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Updates the current user's profile details.
*/
public LiveData<Resource<UserDTO>> updateMe(Map<String, String> updates) {
MutableLiveData<Resource<UserDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
authApi.updateMe(updates).enqueue(new Callback<UserDTO>() {
@Override
public void onResponse(Call<UserDTO> call, Response<UserDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<UserDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Uploads a multipart image to be used as the current user's avatar.
*/
public LiveData<Resource<UserDTO>> uploadAvatar(MultipartBody.Part avatar) {
MutableLiveData<Resource<UserDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
authApi.uploadAvatar(avatar).enqueue(new Callback<UserDTO>() {
@Override
public void onResponse(Call<UserDTO> call, Response<UserDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<UserDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to remove the current user's avatar.
*/
public LiveData<Resource<Void>> deleteAvatar() {
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
authApi.deleteAvatar().enqueue(new Callback<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> response) {
if (response.isSuccessful()) {
data.setValue(Resource.success(null));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<Void> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Clears all authentication and login data from storage.
*/
public void logout() {
tokenManager.clearLoginData();
}
}

View File

@@ -0,0 +1,52 @@
package com.example.petstoremobile.repositories;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.example.petstoremobile.api.CategoryApi;
import com.example.petstoremobile.dtos.CategoryDTO;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.utils.Resource;
import javax.inject.Inject;
import javax.inject.Singleton;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
@Singleton
public class CategoryRepository {
private final CategoryApi categoryApi;
@Inject
public CategoryRepository(CategoryApi categoryApi) {
this.categoryApi = categoryApi;
}
/**
* Retrieves a paginated list of all product categories from the API.
*/
public LiveData<Resource<PageResponse<CategoryDTO>>> getAllCategories(int page, int size) {
MutableLiveData<Resource<PageResponse<CategoryDTO>>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
categoryApi.getAllCategories(page, size).enqueue(new Callback<PageResponse<CategoryDTO>>() {
@Override
public void onResponse(Call<PageResponse<CategoryDTO>> call, Response<PageResponse<CategoryDTO>> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<PageResponse<CategoryDTO>> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
}

View File

@@ -0,0 +1,184 @@
package com.example.petstoremobile.repositories;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.example.petstoremobile.api.InventoryApi;
import com.example.petstoremobile.dtos.BulkDeleteRequest;
import com.example.petstoremobile.dtos.InventoryDTO;
import com.example.petstoremobile.dtos.InventoryRequest;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.utils.Resource;
import javax.inject.Inject;
import javax.inject.Singleton;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
@Singleton
public class InventoryRepository {
private final InventoryApi inventoryApi;
@Inject
public InventoryRepository(InventoryApi inventoryApi) {
this.inventoryApi = inventoryApi;
}
/**
* Retrieves a paginated list of inventory items from the API with optional search and sort.
*/
public LiveData<Resource<PageResponse<InventoryDTO>>> getAllInventory(String query, int page, int size, String sort) {
MutableLiveData<Resource<PageResponse<InventoryDTO>>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
inventoryApi.getAllInventory(query, page, size, sort).enqueue(new Callback<PageResponse<InventoryDTO>>() {
@Override
public void onResponse(Call<PageResponse<InventoryDTO>> call, Response<PageResponse<InventoryDTO>> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<PageResponse<InventoryDTO>> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Retrieves a specific inventory item by its ID from the API.
*/
public LiveData<Resource<InventoryDTO>> getInventoryById(Long id) {
MutableLiveData<Resource<InventoryDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
inventoryApi.getInventoryById(id).enqueue(new Callback<InventoryDTO>() {
@Override
public void onResponse(Call<InventoryDTO> call, Response<InventoryDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<InventoryDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to create a new inventory record.
*/
public LiveData<Resource<InventoryDTO>> createInventory(InventoryRequest request) {
MutableLiveData<Resource<InventoryDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
inventoryApi.createInventory(request).enqueue(new Callback<InventoryDTO>() {
@Override
public void onResponse(Call<InventoryDTO> call, Response<InventoryDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<InventoryDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to update an existing inventory record.
*/
public LiveData<Resource<InventoryDTO>> updateInventory(Long id, InventoryRequest request) {
MutableLiveData<Resource<InventoryDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
inventoryApi.updateInventory(id, request).enqueue(new Callback<InventoryDTO>() {
@Override
public void onResponse(Call<InventoryDTO> call, Response<InventoryDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<InventoryDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to delete a specific inventory record.
*/
public LiveData<Resource<Void>> deleteInventory(Long id) {
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
inventoryApi.deleteInventory(id).enqueue(new Callback<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> response) {
if (response.isSuccessful()) {
data.setValue(Resource.success(null));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<Void> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to delete multiple inventory records at once.
*/
public LiveData<Resource<Void>> bulkDeleteInventory(BulkDeleteRequest request) {
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
inventoryApi.bulkDeleteInventory(request).enqueue(new Callback<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> response) {
if (response.isSuccessful()) {
data.setValue(Resource.success(null));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<Void> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
}

View File

@@ -0,0 +1,208 @@
package com.example.petstoremobile.repositories;
import com.example.petstoremobile.api.PetApi;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.dtos.PetDTO;
import com.example.petstoremobile.utils.Resource;
import javax.inject.Inject;
import javax.inject.Singleton;
import okhttp3.MultipartBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
@Singleton
public class PetRepository {
private final PetApi petApi;
@Inject
public PetRepository(PetApi petApi) {
this.petApi = petApi;
}
/**
* Retrieves a paginated list of all pets from the API.
*/
public LiveData<Resource<PageResponse<PetDTO>>> getAllPets(int page, int size) {
MutableLiveData<Resource<PageResponse<PetDTO>>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
petApi.getAllPets(page, size).enqueue(new Callback<PageResponse<PetDTO>>() {
@Override
public void onResponse(Call<PageResponse<PetDTO>> call, Response<PageResponse<PetDTO>> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<PageResponse<PetDTO>> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Retrieves a specific pet by its ID from the API.
*/
public LiveData<Resource<PetDTO>> getPetById(Long id) {
MutableLiveData<Resource<PetDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
petApi.getPetById(id).enqueue(new Callback<PetDTO>() {
@Override
public void onResponse(Call<PetDTO> call, Response<PetDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<PetDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to create a new pet record.
*/
public LiveData<Resource<PetDTO>> createPet(PetDTO pet) {
MutableLiveData<Resource<PetDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
petApi.createPet(pet).enqueue(new Callback<PetDTO>() {
@Override
public void onResponse(Call<PetDTO> call, Response<PetDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<PetDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to update an existing pet record.
*/
public LiveData<Resource<PetDTO>> updatePet(Long id, PetDTO pet) {
MutableLiveData<Resource<PetDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
petApi.updatePet(id, pet).enqueue(new Callback<PetDTO>() {
@Override
public void onResponse(Call<PetDTO> call, Response<PetDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<PetDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to delete a specific pet record.
*/
public LiveData<Resource<Void>> deletePet(Long id) {
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
petApi.deletePet(id).enqueue(new Callback<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> response) {
if (response.isSuccessful()) {
data.setValue(Resource.success(null));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<Void> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Uploads an image file for a specific pet via the API.
*/
public LiveData<Resource<Void>> uploadPetImage(Long id, MultipartBody.Part image) {
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
petApi.uploadPetImage(id, image).enqueue(new Callback<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> response) {
if (response.isSuccessful()) {
data.setValue(Resource.success(null));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<Void> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to delete the image of a specific pet.
*/
public LiveData<Resource<Void>> deletePetImage(Long id) {
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
petApi.deletePetImage(id).enqueue(new Callback<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> response) {
if (response.isSuccessful()) {
data.setValue(Resource.success(null));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<Void> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
}

View File

@@ -0,0 +1,209 @@
package com.example.petstoremobile.repositories;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.example.petstoremobile.api.ProductApi;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.dtos.ProductDTO;
import com.example.petstoremobile.utils.Resource;
import javax.inject.Inject;
import javax.inject.Singleton;
import okhttp3.MultipartBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
@Singleton
public class ProductRepository {
private final ProductApi productApi;
@Inject
public ProductRepository(ProductApi productApi) {
this.productApi = productApi;
}
/**
* Retrieves a paginated list of products from the API, filtered by an optional query.
*/
public LiveData<Resource<PageResponse<ProductDTO>>> getAllProducts(String query, int page, int size) {
MutableLiveData<Resource<PageResponse<ProductDTO>>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
productApi.getAllProducts(query, page, size).enqueue(new Callback<PageResponse<ProductDTO>>() {
@Override
public void onResponse(Call<PageResponse<ProductDTO>> call, Response<PageResponse<ProductDTO>> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<PageResponse<ProductDTO>> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Retrieves a specific product by its ID from the API.
*/
public LiveData<Resource<ProductDTO>> getProductById(Long id) {
MutableLiveData<Resource<ProductDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
productApi.getProductById(id).enqueue(new Callback<ProductDTO>() {
@Override
public void onResponse(Call<ProductDTO> call, Response<ProductDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<ProductDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to create a new product.
*/
public LiveData<Resource<ProductDTO>> createProduct(ProductDTO product) {
MutableLiveData<Resource<ProductDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
productApi.createProduct(product).enqueue(new Callback<ProductDTO>() {
@Override
public void onResponse(Call<ProductDTO> call, Response<ProductDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<ProductDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to update an existing product by ID.
*/
public LiveData<Resource<ProductDTO>> updateProduct(Long id, ProductDTO product) {
MutableLiveData<Resource<ProductDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
productApi.updateProduct(id, product).enqueue(new Callback<ProductDTO>() {
@Override
public void onResponse(Call<ProductDTO> call, Response<ProductDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<ProductDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to delete a specific product.
*/
public LiveData<Resource<Void>> deleteProduct(Long id) {
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
productApi.deleteProduct(id).enqueue(new Callback<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> response) {
if (response.isSuccessful()) {
data.setValue(Resource.success(null));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<Void> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Uploads an image file for a specific product via the API.
*/
public LiveData<Resource<Void>> uploadProductImage(Long id, MultipartBody.Part image) {
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
productApi.uploadProductImage(id, image).enqueue(new Callback<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> response) {
if (response.isSuccessful()) {
data.setValue(Resource.success(null));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<Void> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to delete the image of a specific product.
*/
public LiveData<Resource<Void>> deleteProductImage(Long id) {
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
productApi.deleteProductImage(id).enqueue(new Callback<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> response) {
if (response.isSuccessful()) {
data.setValue(Resource.success(null));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<Void> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
}

View File

@@ -0,0 +1,130 @@
package com.example.petstoremobile.repositories;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.example.petstoremobile.api.ProductSupplierApi;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.dtos.ProductSupplierDTO;
import com.example.petstoremobile.utils.Resource;
import javax.inject.Inject;
import javax.inject.Singleton;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
@Singleton
public class ProductSupplierRepository {
private final ProductSupplierApi api;
@Inject
public ProductSupplierRepository(ProductSupplierApi api) {
this.api = api;
}
/**
* Retrieves a paginated list of all product-supplier relationships from the API.
*/
public LiveData<Resource<PageResponse<ProductSupplierDTO>>> getAllProductSuppliers(int page, int size) {
MutableLiveData<Resource<PageResponse<ProductSupplierDTO>>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
api.getAllProductSuppliers(page, size).enqueue(new Callback<PageResponse<ProductSupplierDTO>>() {
@Override
public void onResponse(Call<PageResponse<ProductSupplierDTO>> call, Response<PageResponse<ProductSupplierDTO>> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<PageResponse<ProductSupplierDTO>> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to create a new product-supplier relationship.
*/
public LiveData<Resource<ProductSupplierDTO>> createProductSupplier(ProductSupplierDTO dto) {
MutableLiveData<Resource<ProductSupplierDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
api.createProductSupplier(dto).enqueue(new Callback<ProductSupplierDTO>() {
@Override
public void onResponse(Call<ProductSupplierDTO> call, Response<ProductSupplierDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<ProductSupplierDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to update an existing product-supplier relationship.
*/
public LiveData<Resource<ProductSupplierDTO>> updateProductSupplier(Long productId, Long supplierId, ProductSupplierDTO dto) {
MutableLiveData<Resource<ProductSupplierDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
api.updateProductSupplier(productId, supplierId, dto).enqueue(new Callback<ProductSupplierDTO>() {
@Override
public void onResponse(Call<ProductSupplierDTO> call, Response<ProductSupplierDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<ProductSupplierDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to delete a specific product-supplier relationship.
*/
public LiveData<Resource<Void>> deleteProductSupplier(Long productId, Long supplierId) {
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
api.deleteProductSupplier(productId, supplierId).enqueue(new Callback<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> response) {
if (response.isSuccessful()) {
data.setValue(Resource.success(null));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<Void> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
}

View File

@@ -0,0 +1,78 @@
package com.example.petstoremobile.repositories;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.example.petstoremobile.api.PurchaseOrderApi;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.dtos.PurchaseOrderDTO;
import com.example.petstoremobile.utils.Resource;
import javax.inject.Inject;
import javax.inject.Singleton;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
@Singleton
public class PurchaseOrderRepository {
private final PurchaseOrderApi api;
@Inject
public PurchaseOrderRepository(PurchaseOrderApi api) {
this.api = api;
}
/**
* Retrieves a paginated list of all purchase orders from the API.
*/
public LiveData<Resource<PageResponse<PurchaseOrderDTO>>> getAllPurchaseOrders(int page, int size) {
MutableLiveData<Resource<PageResponse<PurchaseOrderDTO>>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
api.getAllPurchaseOrders(page, size).enqueue(new Callback<PageResponse<PurchaseOrderDTO>>() {
@Override
public void onResponse(Call<PageResponse<PurchaseOrderDTO>> call, Response<PageResponse<PurchaseOrderDTO>> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<PageResponse<PurchaseOrderDTO>> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Retrieves a specific purchase order by its ID from the API.
*/
public LiveData<Resource<PurchaseOrderDTO>> getPurchaseOrderById(Long id) {
MutableLiveData<Resource<PurchaseOrderDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
api.getPurchaseOrderById(id).enqueue(new Callback<PurchaseOrderDTO>() {
@Override
public void onResponse(Call<PurchaseOrderDTO> call, Response<PurchaseOrderDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<PurchaseOrderDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
}

View File

@@ -0,0 +1,156 @@
package com.example.petstoremobile.repositories;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.example.petstoremobile.api.ServiceApi;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.dtos.ServiceDTO;
import com.example.petstoremobile.utils.Resource;
import javax.inject.Inject;
import javax.inject.Singleton;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
@Singleton
public class ServiceRepository {
private final ServiceApi serviceApi;
@Inject
public ServiceRepository(ServiceApi serviceApi) {
this.serviceApi = serviceApi;
}
/**
* Retrieves a paginated list of all services from the API.
*/
public LiveData<Resource<PageResponse<ServiceDTO>>> getAllServices(int page, int size) {
MutableLiveData<Resource<PageResponse<ServiceDTO>>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
serviceApi.getAllServices(page, size).enqueue(new Callback<PageResponse<ServiceDTO>>() {
@Override
public void onResponse(Call<PageResponse<ServiceDTO>> call, Response<PageResponse<ServiceDTO>> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<PageResponse<ServiceDTO>> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Retrieves a specific service by its ID from the API.
*/
public LiveData<Resource<ServiceDTO>> getServiceById(Long id) {
MutableLiveData<Resource<ServiceDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
serviceApi.getServiceById(id).enqueue(new Callback<ServiceDTO>() {
@Override
public void onResponse(Call<ServiceDTO> call, Response<ServiceDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<ServiceDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to create a new service.
*/
public LiveData<Resource<ServiceDTO>> createService(ServiceDTO service) {
MutableLiveData<Resource<ServiceDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
serviceApi.createService(service).enqueue(new Callback<ServiceDTO>() {
@Override
public void onResponse(Call<ServiceDTO> call, Response<ServiceDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<ServiceDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to update an existing service by ID.
*/
public LiveData<Resource<ServiceDTO>> updateService(Long id, ServiceDTO service) {
MutableLiveData<Resource<ServiceDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
serviceApi.updateService(id, service).enqueue(new Callback<ServiceDTO>() {
@Override
public void onResponse(Call<ServiceDTO> call, Response<ServiceDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<ServiceDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to delete a specific service.
*/
public LiveData<Resource<Void>> deleteService(Long id) {
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
serviceApi.deleteService(id).enqueue(new Callback<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> response) {
if (response.isSuccessful()) {
data.setValue(Resource.success(null));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<Void> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
}

View File

@@ -0,0 +1,156 @@
package com.example.petstoremobile.repositories;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.example.petstoremobile.api.SupplierApi;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.dtos.SupplierDTO;
import com.example.petstoremobile.utils.Resource;
import javax.inject.Inject;
import javax.inject.Singleton;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
@Singleton
public class SupplierRepository {
private final SupplierApi supplierApi;
@Inject
public SupplierRepository(SupplierApi supplierApi) {
this.supplierApi = supplierApi;
}
/**
* Retrieves a paginated list of all suppliers from the API.
*/
public LiveData<Resource<PageResponse<SupplierDTO>>> getAllSuppliers(int page, int size) {
MutableLiveData<Resource<PageResponse<SupplierDTO>>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
supplierApi.getAllSuppliers(page, size).enqueue(new Callback<PageResponse<SupplierDTO>>() {
@Override
public void onResponse(Call<PageResponse<SupplierDTO>> call, Response<PageResponse<SupplierDTO>> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<PageResponse<SupplierDTO>> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Retrieves a specific supplier by its ID from the API.
*/
public LiveData<Resource<SupplierDTO>> getSupplierById(Long id) {
MutableLiveData<Resource<SupplierDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
supplierApi.getSupplierById(id).enqueue(new Callback<SupplierDTO>() {
@Override
public void onResponse(Call<SupplierDTO> call, Response<SupplierDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<SupplierDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to create a new supplier record.
*/
public LiveData<Resource<SupplierDTO>> createSupplier(SupplierDTO supplier) {
MutableLiveData<Resource<SupplierDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
supplierApi.createSupplier(supplier).enqueue(new Callback<SupplierDTO>() {
@Override
public void onResponse(Call<SupplierDTO> call, Response<SupplierDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<SupplierDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to update an existing supplier record by ID.
*/
public LiveData<Resource<SupplierDTO>> updateSupplier(Long id, SupplierDTO supplier) {
MutableLiveData<Resource<SupplierDTO>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
supplierApi.updateSupplier(id, supplier).enqueue(new Callback<SupplierDTO>() {
@Override
public void onResponse(Call<SupplierDTO> call, Response<SupplierDTO> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(Resource.success(response.body()));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<SupplierDTO> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
/**
* Sends a request to the API to delete a specific supplier record.
*/
public LiveData<Resource<Void>> deleteSupplier(Long id) {
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
data.setValue(Resource.loading(null));
supplierApi.deleteSupplier(id).enqueue(new Callback<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> response) {
if (response.isSuccessful()) {
data.setValue(Resource.success(null));
} else {
data.setValue(Resource.error("Error: " + response.message(), null));
}
}
@Override
public void onFailure(Call<Void> call, Throwable t) {
data.setValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
}

View File

@@ -0,0 +1,27 @@
package com.example.petstoremobile.utils;
import android.content.Context;
import android.net.Uri;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
public class FileUtils {
public static File getFileFromUri(Context context, Uri uri) {
try {
InputStream inputStream = context.getContentResolver().openInputStream(uri);
File tempFile = new File(context.getCacheDir(), "upload_image_" + System.currentTimeMillis() + ".jpg");
FileOutputStream outputStream = new FileOutputStream(tempFile);
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
outputStream.close();
inputStream.close();
return tempFile;
} catch (Exception e) {
return null;
}
}
}

View File

@@ -0,0 +1,30 @@
package com.example.petstoremobile.utils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class Resource<T> {
public enum Status { SUCCESS, ERROR, LOADING }
public final Status status;
public final T data;
public final String message;
private Resource(Status status, @Nullable T data, @Nullable String message) {
this.status = status;
this.data = data;
this.message = message;
}
public static <T> Resource<T> success(@Nullable T data) {
return new Resource<>(Status.SUCCESS, data, null);
}
public static <T> Resource<T> error(String msg, @Nullable T data) {
return new Resource<>(Status.ERROR, data, msg);
}
public static <T> Resource<T> loading(@Nullable T data) {
return new Resource<>(Status.LOADING, data, null);
}
}

View File

@@ -0,0 +1,58 @@
package com.example.petstoremobile.viewmodels;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import com.example.petstoremobile.dtos.AdoptionDTO;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.repositories.AdoptionRepository;
import com.example.petstoremobile.utils.Resource;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
@HiltViewModel
public class AdoptionViewModel extends ViewModel {
private final AdoptionRepository repository;
@Inject
public AdoptionViewModel(AdoptionRepository repository) {
this.repository = repository;
}
/**
* Fetches a paginated list of all adoptions.
*/
public LiveData<Resource<PageResponse<AdoptionDTO>>> getAllAdoptions(int page, int size) {
return repository.getAllAdoptions(page, size);
}
/**
* Retrieves a single adoption by its ID.
*/
public LiveData<Resource<AdoptionDTO>> getAdoptionById(Long id) {
return repository.getAdoptionById(id);
}
/**
* Creates a new adoption record.
*/
public LiveData<Resource<AdoptionDTO>> createAdoption(AdoptionDTO adoption) {
return repository.createAdoption(adoption);
}
/**
* Updates an existing adoption record by ID.
*/
public LiveData<Resource<AdoptionDTO>> updateAdoption(Long id, AdoptionDTO adoption) {
return repository.updateAdoption(id, adoption);
}
/**
* Deletes an adoption record by ID.
*/
public LiveData<Resource<Void>> deleteAdoption(Long id) {
return repository.deleteAdoption(id);
}
}

View File

@@ -0,0 +1,58 @@
package com.example.petstoremobile.viewmodels;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import com.example.petstoremobile.dtos.AppointmentDTO;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.repositories.AppointmentRepository;
import com.example.petstoremobile.utils.Resource;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
@HiltViewModel
public class AppointmentViewModel extends ViewModel {
private final AppointmentRepository repository;
@Inject
public AppointmentViewModel(AppointmentRepository repository) {
this.repository = repository;
}
/**
* Fetches a paginated list of all appointments.
*/
public LiveData<Resource<PageResponse<AppointmentDTO>>> getAllAppointments(int page, int size) {
return repository.getAllAppointments(page, size);
}
/**
* Retrieves a single appointment by its ID.
*/
public LiveData<Resource<AppointmentDTO>> getAppointmentById(Long id) {
return repository.getAppointmentById(id);
}
/**
* Creates a new appointment.
*/
public LiveData<Resource<AppointmentDTO>> createAppointment(AppointmentDTO appointment) {
return repository.createAppointment(appointment);
}
/**
* Updates an existing appointment record by ID.
*/
public LiveData<Resource<AppointmentDTO>> updateAppointment(Long id, AppointmentDTO appointment) {
return repository.updateAppointment(id, appointment);
}
/**
* Deletes an appointment record by ID.
*/
public LiveData<Resource<Void>> deleteAppointment(Long id) {
return repository.deleteAppointment(id);
}
}

View File

@@ -0,0 +1,68 @@
package com.example.petstoremobile.viewmodels;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import com.example.petstoremobile.dtos.AuthDTO;
import com.example.petstoremobile.dtos.UserDTO;
import com.example.petstoremobile.repositories.AuthRepository;
import com.example.petstoremobile.utils.Resource;
import java.util.Map;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
import okhttp3.MultipartBody;
@HiltViewModel
public class AuthViewModel extends ViewModel {
private final AuthRepository repository;
@Inject
public AuthViewModel(AuthRepository repository) {
this.repository = repository;
}
/**
* Authenticates a user with username and password.
*/
public LiveData<Resource<AuthDTO.LoginResponse>> login(String username, String password) {
return repository.login(new AuthDTO.LoginRequest(username, password));
}
/**
* Retrieves the profile information of the currently authenticated user.
*/
public LiveData<Resource<UserDTO>> getMe() {
return repository.getMe();
}
/**
* Updates the profile information of the current user.
*/
public LiveData<Resource<UserDTO>> updateMe(Map<String, String> updates) {
return repository.updateMe(updates);
}
/**
* Uploads a new avatar image for the current user.
*/
public LiveData<Resource<UserDTO>> uploadAvatar(MultipartBody.Part avatar) {
return repository.uploadAvatar(avatar);
}
/**
* Deletes the avatar image of the current user.
*/
public LiveData<Resource<Void>> deleteAvatar() {
return repository.deleteAvatar();
}
/**
* Logs out the current user by clearing stored credentials.
*/
public void logout() {
repository.logout();
}
}

View File

@@ -0,0 +1,80 @@
package com.example.petstoremobile.viewmodels;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import com.example.petstoremobile.dtos.BulkDeleteRequest;
import com.example.petstoremobile.dtos.CategoryDTO;
import com.example.petstoremobile.dtos.InventoryDTO;
import com.example.petstoremobile.dtos.InventoryRequest;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.repositories.CategoryRepository;
import com.example.petstoremobile.repositories.InventoryRepository;
import com.example.petstoremobile.utils.Resource;
import java.util.List;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
@HiltViewModel
public class InventoryViewModel extends ViewModel {
private final InventoryRepository inventoryRepository;
private final CategoryRepository categoryRepository;
@Inject
public InventoryViewModel(InventoryRepository inventoryRepository, CategoryRepository categoryRepository) {
this.inventoryRepository = inventoryRepository;
this.categoryRepository = categoryRepository;
}
/**
* Retrieves a paginated list of inventory items, with optional filtering and sorting.
*/
public LiveData<Resource<PageResponse<InventoryDTO>>> getAllInventory(String query, int page, int size, String sort) {
return inventoryRepository.getAllInventory(query, page, size, sort);
}
/**
* Retrieves a single inventory item by its ID.
*/
public LiveData<Resource<InventoryDTO>> getInventoryById(Long id) {
return inventoryRepository.getInventoryById(id);
}
/**
* Creates a new inventory record.
*/
public LiveData<Resource<InventoryDTO>> createInventory(InventoryRequest request) {
return inventoryRepository.createInventory(request);
}
/**
* Updates an existing inventory record by ID.
*/
public LiveData<Resource<InventoryDTO>> updateInventory(Long id, InventoryRequest request) {
return inventoryRepository.updateInventory(id, request);
}
/**
* Deletes an inventory record by ID.
*/
public LiveData<Resource<Void>> deleteInventory(Long id) {
return inventoryRepository.deleteInventory(id);
}
/**
* Deletes multiple inventory records in a single request.
*/
public LiveData<Resource<Void>> bulkDeleteInventory(List<Long> ids) {
return inventoryRepository.bulkDeleteInventory(new BulkDeleteRequest(ids));
}
/**
* Retrieves a paginated list of categories.
*/
public LiveData<Resource<PageResponse<CategoryDTO>>> getAllCategories(int page, int size) {
return categoryRepository.getAllCategories(page, size);
}
}

View File

@@ -0,0 +1,75 @@
package com.example.petstoremobile.viewmodels;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.dtos.PetDTO;
import com.example.petstoremobile.repositories.PetRepository;
import com.example.petstoremobile.utils.Resource;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
import okhttp3.MultipartBody;
@HiltViewModel
public class PetViewModel extends ViewModel {
private final PetRepository repository;
private final MutableLiveData<Long> _petId = new MutableLiveData<>();
@Inject
public PetViewModel(PetRepository repository) {
this.repository = repository;
}
/**
* Fetches a paginated list of all pets.
*/
public LiveData<Resource<PageResponse<PetDTO>>> getAllPets(int page, int size) {
return repository.getAllPets(page, size);
}
/**
* Retrieves a single pet by its ID.
*/
public LiveData<Resource<PetDTO>> getPetById(Long id) {
return repository.getPetById(id);
}
/**
* Creates a new pet record.
*/
public LiveData<Resource<PetDTO>> createPet(PetDTO pet) {
return repository.createPet(pet);
}
/**
* Updates an existing pet record by ID.
*/
public LiveData<Resource<PetDTO>> updatePet(Long id, PetDTO pet) {
return repository.updatePet(id, pet);
}
/**
* Deletes a pet record by ID.
*/
public LiveData<Resource<Void>> deletePet(Long id) {
return repository.deletePet(id);
}
/**
* Uploads an image for a specific pet.
*/
public LiveData<Resource<Void>> uploadPetImage(Long id, MultipartBody.Part image) {
return repository.uploadPetImage(id, image);
}
/**
* Deletes the image associated with a specific pet.
*/
public LiveData<Resource<Void>> deletePetImage(Long id) {
return repository.deletePetImage(id);
}
}

View File

@@ -0,0 +1,51 @@
package com.example.petstoremobile.viewmodels;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.dtos.ProductSupplierDTO;
import com.example.petstoremobile.repositories.ProductSupplierRepository;
import com.example.petstoremobile.utils.Resource;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
@HiltViewModel
public class ProductSupplierViewModel extends ViewModel {
private final ProductSupplierRepository repository;
@Inject
public ProductSupplierViewModel(ProductSupplierRepository repository) {
this.repository = repository;
}
/**
* Fetches a paginated list of all product-supplier relationships.
*/
public LiveData<Resource<PageResponse<ProductSupplierDTO>>> getAllProductSuppliers(int page, int size) {
return repository.getAllProductSuppliers(page, size);
}
/**
* Creates a new product-supplier relationship.
*/
public LiveData<Resource<ProductSupplierDTO>> createProductSupplier(ProductSupplierDTO dto) {
return repository.createProductSupplier(dto);
}
/**
* Updates an existing product-supplier relationship.
*/
public LiveData<Resource<ProductSupplierDTO>> updateProductSupplier(Long productId, Long supplierId, ProductSupplierDTO dto) {
return repository.updateProductSupplier(productId, supplierId, dto);
}
/**
* Deletes a product-supplier relationship by product and supplier IDs.
*/
public LiveData<Resource<Void>> deleteProductSupplier(Long productId, Long supplierId) {
return repository.deleteProductSupplier(productId, supplierId);
}
}

View File

@@ -0,0 +1,84 @@
package com.example.petstoremobile.viewmodels;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import com.example.petstoremobile.dtos.CategoryDTO;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.dtos.ProductDTO;
import com.example.petstoremobile.repositories.CategoryRepository;
import com.example.petstoremobile.repositories.ProductRepository;
import com.example.petstoremobile.utils.Resource;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
import okhttp3.MultipartBody;
@HiltViewModel
public class ProductViewModel extends ViewModel {
private final ProductRepository productRepository;
private final CategoryRepository categoryRepository;
@Inject
public ProductViewModel(ProductRepository productRepository, CategoryRepository categoryRepository) {
this.productRepository = productRepository;
this.categoryRepository = categoryRepository;
}
/**
* Retrieves a paginated list of products, optionally filtered by a query string.
*/
public LiveData<Resource<PageResponse<ProductDTO>>> getAllProducts(String query, int page, int size) {
return productRepository.getAllProducts(query, page, size);
}
/**
* Retrieves a single product by its ID.
*/
public LiveData<Resource<ProductDTO>> getProductById(Long id) {
return productRepository.getProductById(id);
}
/**
* Creates a new product.
*/
public LiveData<Resource<ProductDTO>> createProduct(ProductDTO product) {
return productRepository.createProduct(product);
}
/**
* Updates an existing product by ID.
*/
public LiveData<Resource<ProductDTO>> updateProduct(Long id, ProductDTO product) {
return productRepository.updateProduct(id, product);
}
/**
* Deletes a product by its ID.
*/
public LiveData<Resource<Void>> deleteProduct(Long id) {
return productRepository.deleteProduct(id);
}
/**
* Uploads an image for a specific product.
*/
public LiveData<Resource<Void>> uploadProductImage(Long id, MultipartBody.Part image) {
return productRepository.uploadProductImage(id, image);
}
/**
* Deletes the image associated with a specific product.
*/
public LiveData<Resource<Void>> deleteProductImage(Long id) {
return productRepository.deleteProductImage(id);
}
/**
* Retrieves a paginated list of all product categories.
*/
public LiveData<Resource<PageResponse<CategoryDTO>>> getAllCategories(int page, int size) {
return categoryRepository.getAllCategories(page, size);
}
}

View File

@@ -0,0 +1,37 @@
package com.example.petstoremobile.viewmodels;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.dtos.PurchaseOrderDTO;
import com.example.petstoremobile.repositories.PurchaseOrderRepository;
import com.example.petstoremobile.utils.Resource;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
@HiltViewModel
public class PurchaseOrderViewModel extends ViewModel {
private final PurchaseOrderRepository repository;
@Inject
public PurchaseOrderViewModel(PurchaseOrderRepository repository) {
this.repository = repository;
}
/**
* Fetches a paginated list of all purchase orders.
*/
public LiveData<Resource<PageResponse<PurchaseOrderDTO>>> getAllPurchaseOrders(int page, int size) {
return repository.getAllPurchaseOrders(page, size);
}
/**
* Retrieves a single purchase order by its ID.
*/
public LiveData<Resource<PurchaseOrderDTO>> getPurchaseOrderById(Long id) {
return repository.getPurchaseOrderById(id);
}
}

View File

@@ -0,0 +1,58 @@
package com.example.petstoremobile.viewmodels;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.dtos.ServiceDTO;
import com.example.petstoremobile.repositories.ServiceRepository;
import com.example.petstoremobile.utils.Resource;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
@HiltViewModel
public class ServiceViewModel extends ViewModel {
private final ServiceRepository repository;
@Inject
public ServiceViewModel(ServiceRepository repository) {
this.repository = repository;
}
/**
* Fetches a paginated list of all services.
*/
public LiveData<Resource<PageResponse<ServiceDTO>>> getAllServices(int page, int size) {
return repository.getAllServices(page, size);
}
/**
* Retrieves a single service by its ID.
*/
public LiveData<Resource<ServiceDTO>> getServiceById(Long id) {
return repository.getServiceById(id);
}
/**
* Creates a new service.
*/
public LiveData<Resource<ServiceDTO>> createService(ServiceDTO service) {
return repository.createService(service);
}
/**
* Updates an existing service by ID.
*/
public LiveData<Resource<ServiceDTO>> updateService(Long id, ServiceDTO service) {
return repository.updateService(id, service);
}
/**
* Deletes a service by ID.
*/
public LiveData<Resource<Void>> deleteService(Long id) {
return repository.deleteService(id);
}
}

View File

@@ -0,0 +1,58 @@
package com.example.petstoremobile.viewmodels;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.dtos.SupplierDTO;
import com.example.petstoremobile.repositories.SupplierRepository;
import com.example.petstoremobile.utils.Resource;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
@HiltViewModel
public class SupplierViewModel extends ViewModel {
private final SupplierRepository repository;
@Inject
public SupplierViewModel(SupplierRepository repository) {
this.repository = repository;
}
/**
* Fetches a paginated list of all suppliers.
*/
public LiveData<Resource<PageResponse<SupplierDTO>>> getAllSuppliers(int page, int size) {
return repository.getAllSuppliers(page, size);
}
/**
* Retrieves a single supplier by its ID.
*/
public LiveData<Resource<SupplierDTO>> getSupplierById(Long id) {
return repository.getSupplierById(id);
}
/**
* Creates a new supplier record.
*/
public LiveData<Resource<SupplierDTO>> createSupplier(SupplierDTO supplier) {
return repository.createSupplier(supplier);
}
/**
* Updates an existing supplier record by ID.
*/
public LiveData<Resource<SupplierDTO>> updateSupplier(Long id, SupplierDTO supplier) {
return repository.updateSupplier(id, supplier);
}
/**
* Deletes a supplier record by ID.
*/
public LiveData<Resource<Void>> deleteSupplier(Long id) {
return repository.deleteSupplier(id);
}
}