cleaning code
This commit is contained in:
@@ -214,9 +214,11 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
Toast.makeText(requireContext(), "Downloading " + message.getAttachmentName() + "...", Toast.LENGTH_SHORT).show();
|
||||
|
||||
viewModel.downloadAttachment(message.getId()).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
saveFileToDownloads(resource.data, message.getAttachmentName(), message.getAttachmentMimeType());
|
||||
} else if (resource != null && resource.status == Resource.Status.ERROR) {
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(requireContext(), "Download failed: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
@@ -285,9 +287,13 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
scrollToBottom();
|
||||
});
|
||||
|
||||
viewModel.getIsLoading().observe(getViewLifecycleOwner(), loading -> {
|
||||
// Can show a progress bar if needed
|
||||
});
|
||||
viewModel.getIsLoading().observe(getViewLifecycleOwner(), this::setLoading);
|
||||
}
|
||||
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTitleAndStateIfActive(List<Chat> list) {
|
||||
@@ -324,15 +330,12 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
activeConversationId = getActivity().getIntent().getLongExtra("conversation_id", -1);
|
||||
getActivity().getIntent().removeExtra("conversation_id");
|
||||
} else {
|
||||
// Restore last active conversation if any
|
||||
activeConversationId = viewModel.getLastActiveConversationId();
|
||||
}
|
||||
|
||||
viewModel.loadCustomers();
|
||||
|
||||
//
|
||||
if (activeConversationId != null) {
|
||||
// Re-subscribe and load history if there is an active conversation ID
|
||||
if (stompChatManager != null) stompChatManager.subscribeToConversation(activeConversationId);
|
||||
viewModel.loadMessageHistory(activeConversationId);
|
||||
} else {
|
||||
@@ -360,7 +363,9 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
|
||||
binding.etMessage.setText("");
|
||||
viewModel.sendMessage(activeConversationId, text).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
viewModel.addMessageLocally(resource.data);
|
||||
viewModel.loadConversations();
|
||||
}
|
||||
@@ -416,10 +421,12 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
removeAttachment();
|
||||
|
||||
viewModel.sendMessageWithAttachment(activeConversationId, contentPart, filePart).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
viewModel.addMessageLocally(resource.data);
|
||||
viewModel.loadConversations();
|
||||
} else if (resource != null && resource.status == Resource.Status.ERROR) {
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(requireContext(), "Failed to send attachment: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -172,6 +172,12 @@ public class ProfileFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
@@ -184,6 +190,7 @@ public class ProfileFragment extends Fragment {
|
||||
private void loadProfileData() {
|
||||
viewModel.getMe().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
currentUser = resource.data;
|
||||
|
||||
@@ -229,6 +236,7 @@ public class ProfileFragment extends Fragment {
|
||||
//Call the backend to upload the avatar
|
||||
viewModel.uploadAvatar(body).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), "Avatar updated successfully", Toast.LENGTH_SHORT).show();
|
||||
loadProfileData();
|
||||
@@ -247,6 +255,7 @@ public class ProfileFragment extends Fragment {
|
||||
private void deleteAvatar() {
|
||||
viewModel.deleteAvatar().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
hasImage = false;
|
||||
binding.imgProfile.setImageResource(R.drawable.placeholder);
|
||||
@@ -266,6 +275,7 @@ public class ProfileFragment extends Fragment {
|
||||
|
||||
viewModel.updateMe(updates).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
currentUser = resource.data;
|
||||
Toast.makeText(getContext(), "Profile updated successfully", Toast.LENGTH_SHORT).show();
|
||||
|
||||
@@ -41,6 +41,7 @@ public class AnalyticsFragment extends Fragment {
|
||||
viewModel.getAnalyticsData().observe(getViewLifecycleOwner(), this::computeAndDisplay);
|
||||
|
||||
viewModel.getIsLoading().observe(getViewLifecycleOwner(), loading -> {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
if (loading) {
|
||||
binding.tvTotalRevenue.setText("Loading...");
|
||||
binding.tvTotalTransactions.setText("...");
|
||||
@@ -117,8 +118,6 @@ public class AnalyticsFragment extends Fragment {
|
||||
if (data.employeePerformance != null && !data.employeePerformance.isEmpty()) {
|
||||
BigDecimal maxEmp = data.employeePerformance.get(data.employeePerformance.size() - 1).getValue();
|
||||
if (maxEmp.compareTo(BigDecimal.ZERO) == 0) maxEmp = BigDecimal.ONE;
|
||||
// Sorting is ascending from VM for some reason? Let's check VM... it says b.getValue().compareTo(a.getValue()) which is DESC.
|
||||
// Wait, computeAnalytics sorts them... let's assume DESC as per VM code.
|
||||
maxEmp = data.employeePerformance.get(0).getValue();
|
||||
if (maxEmp.compareTo(BigDecimal.ZERO) == 0) maxEmp = BigDecimal.ONE;
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||
|
||||
import android.app.DatePickerDialog;
|
||||
import android.os.Bundle;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
@@ -12,7 +11,9 @@ import androidx.navigation.fragment.NavHostFragment;
|
||||
|
||||
import com.example.petstoremobile.databinding.FragmentAdoptionDetailBinding;
|
||||
import com.example.petstoremobile.dtos.*;
|
||||
import com.example.petstoremobile.utils.DateTimeUtils;
|
||||
import com.example.petstoremobile.utils.DialogUtils;
|
||||
import com.example.petstoremobile.utils.InputValidator;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.SpinnerUtils;
|
||||
import com.example.petstoremobile.utils.UIUtils;
|
||||
@@ -73,6 +74,12 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
viewModel.getEmployeeList().observe(getViewLifecycleOwner(), list -> refreshEmployeeSpinner());
|
||||
}
|
||||
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
@@ -116,29 +123,27 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void setupDatePicker() {
|
||||
binding.etAdoptionDate.setOnClickListener(v -> {
|
||||
Calendar c = Calendar.getInstance();
|
||||
new DatePickerDialog(requireContext(),
|
||||
(dp, y, m, d) -> binding.etAdoptionDate.setText(
|
||||
String.format("%04d-%02d-%02d", y, m + 1, d)),
|
||||
c.get(Calendar.YEAR),
|
||||
c.get(Calendar.MONTH),
|
||||
c.get(Calendar.DAY_OF_MONTH)).show();
|
||||
});
|
||||
binding.etAdoptionDate.setOnClickListener(v -> UIUtils.showDatePicker(requireContext(), binding.etAdoptionDate, null));
|
||||
}
|
||||
|
||||
private void loadSpinnersData() {
|
||||
viewModel.loadPets().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
viewModel.setPetList(resource.data);
|
||||
}
|
||||
});
|
||||
viewModel.loadCustomers().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
viewModel.setCustomerList(resource.data);
|
||||
}
|
||||
});
|
||||
viewModel.loadStores().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
viewModel.setStoreList(resource.data);
|
||||
}
|
||||
@@ -165,7 +170,9 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
|
||||
private void loadEmployees(Long storeId) {
|
||||
viewModel.loadEmployees(storeId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
viewModel.setEmployeeList(resource.data);
|
||||
}
|
||||
});
|
||||
@@ -183,7 +190,7 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
long adoptionId = a.getLong("adoptionId");
|
||||
viewModel.setAdoptionId(adoptionId);
|
||||
binding.tvAdoptionMode.setText("Edit Adoption");
|
||||
binding.tvAdoptionId.setText("ID: " + adoptionId);
|
||||
binding.tvAdoptionId.setText(DateTimeUtils.formatId(adoptionId));
|
||||
binding.tvAdoptionId.setVisibility(View.VISIBLE);
|
||||
binding.btnDeleteAdoption.setVisibility(View.VISIBLE);
|
||||
loadAdoptionData();
|
||||
@@ -199,6 +206,7 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
private void loadAdoptionData() {
|
||||
viewModel.loadAdoption().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
AdoptionDTO a = resource.data;
|
||||
preselectedPetId = a.getPetId() != null ? a.getPetId() : -1;
|
||||
@@ -224,29 +232,16 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void saveAdoption() {
|
||||
if (binding.spinnerAdoptionCustomer.getSelectedItemPosition() == 0) {
|
||||
Toast.makeText(getContext(), "Select a customer", Toast.LENGTH_SHORT).show(); return;
|
||||
}
|
||||
if (binding.spinnerAdoptionPet.getSelectedItemPosition() == 0) {
|
||||
Toast.makeText(getContext(), "Select a pet", Toast.LENGTH_SHORT).show(); return;
|
||||
}
|
||||
if (binding.spinnerAdoptionStore.getSelectedItemPosition() == 0) {
|
||||
Toast.makeText(getContext(), "Select a store", Toast.LENGTH_SHORT).show(); return;
|
||||
}
|
||||
String date = binding.etAdoptionDate.getText().toString().trim();
|
||||
if (date.isEmpty()) {
|
||||
Toast.makeText(getContext(), "Select a date", Toast.LENGTH_SHORT).show(); return;
|
||||
}
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerAdoptionCustomer, "Customer")) return;
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerAdoptionPet, "Pet")) return;
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerAdoptionStore, "Store")) return;
|
||||
if (!InputValidator.isNotEmpty(binding.etAdoptionDate, "Adoption Date")) return;
|
||||
|
||||
BigDecimal fee = BigDecimal.ZERO;
|
||||
String feeStr = binding.etAdoptionFee.getText().toString().trim();
|
||||
if (!feeStr.isEmpty()) {
|
||||
try {
|
||||
if (!InputValidator.isPositiveDecimal(binding.etAdoptionFee, "Adoption Fee")) return;
|
||||
fee = new BigDecimal(feeStr);
|
||||
} catch (NumberFormatException e) {
|
||||
Toast.makeText(getContext(), "Invalid fee format", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DropdownDTO customer = viewModel.getCustomerList().getValue().get(binding.spinnerAdoptionCustomer.getSelectedItemPosition() - 1);
|
||||
@@ -258,6 +253,7 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
employeeId = viewModel.getEmployeeList().getValue().get(binding.spinnerAdoptionEmployee.getSelectedItemPosition() - 1).getId();
|
||||
}
|
||||
|
||||
String adoptionDate = binding.etAdoptionDate.getText().toString().trim();
|
||||
String status = STATUSES[binding.spinnerAdoptionStatus.getSelectedItemPosition()];
|
||||
|
||||
AdoptionDTO dto = new AdoptionDTO(
|
||||
@@ -265,12 +261,14 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
customer.getId(),
|
||||
employeeId,
|
||||
store.getId(),
|
||||
date,
|
||||
adoptionDate,
|
||||
status,
|
||||
fee
|
||||
);
|
||||
|
||||
viewModel.saveAdoption(dto).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), viewModel.isEditing() ? "Updated" : "Saved", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
@@ -281,8 +279,10 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void confirmDelete() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Adoption", () ->
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Adoption Record", () ->
|
||||
viewModel.deleteAdoption().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), "Deleted", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.example.petstoremobile.dtos.DropdownDTO;
|
||||
import com.example.petstoremobile.dtos.ServiceDTO;
|
||||
import com.example.petstoremobile.utils.DateTimeUtils;
|
||||
import com.example.petstoremobile.utils.DialogUtils;
|
||||
import com.example.petstoremobile.utils.InputValidator;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.SpinnerUtils;
|
||||
import com.example.petstoremobile.utils.UIUtils;
|
||||
@@ -140,6 +141,12 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerStaff, list, DropdownDTO::getLabel, "-- Select Staff --", preselectedStaffId, DropdownDTO::getId));
|
||||
}
|
||||
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyViewState(AppointmentDetailViewModel.ViewState state) {
|
||||
isUpdatingUI = true;
|
||||
|
||||
@@ -189,7 +196,9 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
|
||||
private void loadAppointmentData() {
|
||||
viewModel.loadAppointment().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null || resource.status != Resource.Status.SUCCESS || resource.data == null) return;
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
AppointmentDTO a = resource.data;
|
||||
preselectedPetId = a.getPetId() != null ? a.getPetId() : -1;
|
||||
preselectedServiceId = a.getServiceId() != null ? a.getServiceId() : -1;
|
||||
@@ -205,6 +214,7 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
SpinnerUtils.setSelectionByValue(binding.spinnerAppointmentStatus, DateTimeUtils.formatStatusFromBackend(status));
|
||||
}
|
||||
notifyDateTimeStatusChange();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -221,6 +231,8 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
}
|
||||
|
||||
viewModel.saveAppointment(date, time, status).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
AppointmentDetailViewModel.ViewState state = viewModel.getViewState().getValue();
|
||||
String message = (state != null && state.isEditing) ? "Updated" : "Saved";
|
||||
@@ -233,11 +245,11 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
}
|
||||
|
||||
private boolean validateRequiredFields() {
|
||||
if (binding.spinnerCustomer.getSelectedItemPosition() == 0) return UIUtils.showToast(getContext(), "Select a customer");
|
||||
if (binding.spinnerStore.getSelectedItemPosition() == 0) return UIUtils.showToast(getContext(), "Select a store");
|
||||
if (binding.spinnerPet.getSelectedItemPosition() == 0) return UIUtils.showToast(getContext(), "Select a pet");
|
||||
if (binding.spinnerService.getSelectedItemPosition() == 0) return UIUtils.showToast(getContext(), "Select a service");
|
||||
if (binding.etAppointmentDate.getText().toString().trim().isEmpty()) return UIUtils.showToast(getContext(), "Select a date");
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerCustomer, "Customer")) return false;
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerStore, "Store")) return false;
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerPet, "Pet")) return false;
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerService, "Service")) return false;
|
||||
if (!InputValidator.isNotEmpty(binding.etAppointmentDate, "Appointment Date")) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -261,6 +273,8 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
private void confirmDelete() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Appointment", () ->
|
||||
viewModel.deleteAppointment().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS) navigateBack();
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
@@ -17,9 +16,11 @@ import com.example.petstoremobile.databinding.FragmentInventoryDetailBinding;
|
||||
import com.example.petstoremobile.dtos.DropdownDTO;
|
||||
import com.example.petstoremobile.dtos.InventoryDTO;
|
||||
import com.example.petstoremobile.dtos.ProductDTO;
|
||||
import com.example.petstoremobile.utils.DialogUtils;
|
||||
import com.example.petstoremobile.utils.InputValidator;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.SpinnerUtils;
|
||||
import com.example.petstoremobile.utils.UIUtils;
|
||||
import com.example.petstoremobile.viewmodels.InventoryDetailViewModel;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
@@ -67,6 +68,12 @@ public class InventoryDetailFragment extends Fragment {
|
||||
viewModel.getProductList().observe(getViewLifecycleOwner(), list -> refreshProductSpinner());
|
||||
}
|
||||
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
@@ -75,11 +82,15 @@ public class InventoryDetailFragment extends Fragment {
|
||||
|
||||
private void loadSpinnersData() {
|
||||
viewModel.loadStores().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
viewModel.setStoreList(resource.data);
|
||||
}
|
||||
});
|
||||
viewModel.loadProducts().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
viewModel.setProductList(resource.data.getContent());
|
||||
}
|
||||
@@ -123,6 +134,7 @@ public class InventoryDetailFragment extends Fragment {
|
||||
private void loadInventoryData() {
|
||||
viewModel.loadInventory().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
InventoryDTO inv = resource.data;
|
||||
binding.etQuantity.setText(String.valueOf(inv.getQuantity()));
|
||||
@@ -138,19 +150,9 @@ public class InventoryDetailFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void saveInventory() {
|
||||
if (binding.spinnerInventoryStore.getSelectedItemPosition() == 0) {
|
||||
Toast.makeText(getContext(), "Please select a store", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
if (binding.spinnerInventoryProduct.getSelectedItemPosition() == 0) {
|
||||
Toast.makeText(getContext(), "Please select a product", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!InputValidator.isNotEmpty(binding.etQuantity, "Quantity") ||
|
||||
!InputValidator.isPositiveInteger(binding.etQuantity, "Quantity")) {
|
||||
return;
|
||||
}
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerInventoryStore, "Store")) return;
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerInventoryProduct, "Product")) return;
|
||||
if (!InputValidator.isPositiveInteger(binding.etQuantity, "Quantity")) return;
|
||||
|
||||
int quantity = Integer.parseInt(binding.etQuantity.getText().toString().trim());
|
||||
DropdownDTO store = viewModel.getStoreList().getValue().get(binding.spinnerInventoryStore.getSelectedItemPosition() - 1);
|
||||
@@ -160,6 +162,9 @@ public class InventoryDetailFragment extends Fragment {
|
||||
setButtonsEnabled(false);
|
||||
|
||||
viewModel.saveInventory(request).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status != Resource.Status.LOADING) {
|
||||
setButtonsEnabled(true);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), viewModel.isEditing() ? "Inventory updated" : "Inventory created", Toast.LENGTH_SHORT).show();
|
||||
@@ -167,21 +172,20 @@ public class InventoryDetailFragment extends Fragment {
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void confirmDelete() {
|
||||
new AlertDialog.Builder(requireContext())
|
||||
.setTitle("Delete inventory item?")
|
||||
.setMessage("This cannot be undone.")
|
||||
.setPositiveButton("Delete", (d, w) -> deleteInventory())
|
||||
.setNegativeButton("Cancel", null)
|
||||
.show();
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Inventory Item", this::deleteInventory);
|
||||
}
|
||||
|
||||
private void deleteInventory() {
|
||||
setButtonsEnabled(false);
|
||||
viewModel.deleteInventory().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status != Resource.Status.LOADING) {
|
||||
setButtonsEnabled(true);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), "Inventory deleted", Toast.LENGTH_SHORT).show();
|
||||
@@ -189,6 +193,7 @@ public class InventoryDetailFragment extends Fragment {
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Delete failed: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -197,8 +202,6 @@ public class InventoryDetailFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void setButtonsEnabled(boolean enabled) {
|
||||
binding.btnSaveInventory.setEnabled(enabled);
|
||||
binding.btnDeleteInventory.setEnabled(enabled);
|
||||
binding.btnInventoryBack.setEnabled(enabled);
|
||||
UIUtils.setViewsEnabled(enabled, binding.btnSaveInventory, binding.btnDeleteInventory, binding.btnInventoryBack);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.example.petstoremobile.databinding.FragmentPetDetailBinding;
|
||||
import com.example.petstoremobile.dtos.DropdownDTO;
|
||||
import com.example.petstoremobile.dtos.PetDTO;
|
||||
import com.example.petstoremobile.utils.ActivityLogger;
|
||||
import com.example.petstoremobile.utils.DateTimeUtils;
|
||||
import com.example.petstoremobile.utils.DialogUtils;
|
||||
import com.example.petstoremobile.utils.InputValidator;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
@@ -65,7 +66,6 @@ public class PetDetailFragment extends Fragment {
|
||||
observeViewModel();
|
||||
handleArguments();
|
||||
|
||||
//set button click listeners
|
||||
binding.btnBack.setOnClickListener(v -> navigateBack());
|
||||
binding.btnSavePet.setOnClickListener(v -> savePet());
|
||||
binding.btnDeletePet.setOnClickListener(v -> deletePet());
|
||||
@@ -76,36 +76,41 @@ public class PetDetailFragment extends Fragment {
|
||||
viewModel.getStoreList().observe(getViewLifecycleOwner(), list -> updateStoreSpinnerSelection());
|
||||
|
||||
viewModel.loadCustomers().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
viewModel.setCustomerList(resource.data);
|
||||
}
|
||||
});
|
||||
|
||||
viewModel.loadStores().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
viewModel.setStoreList(resource.data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the saving of pet data (adding/updating).
|
||||
*/
|
||||
private void savePet() {
|
||||
// Validates all fields using InputValidator
|
||||
if (!InputValidator.isNotEmpty(binding.etPetName, "Pet Name")) return;
|
||||
if (!InputValidator.isNotEmpty(binding.etPetSpecies, "Species")) return;
|
||||
if (!InputValidator.isNotEmpty(binding.etPetBreed, "Breed")) return;
|
||||
if (!InputValidator.isPositiveInteger(binding.etPetAge, "Age")) return;
|
||||
if (!InputValidator.isPositiveDecimal(binding.etPetPrice, "Price")) return;
|
||||
|
||||
//get all the values from the fields
|
||||
String name = binding.etPetName.getText().toString().trim();
|
||||
String species = binding.etPetSpecies.getText().toString().trim();
|
||||
String breed = binding.etPetBreed.getText().toString().trim();
|
||||
@@ -113,37 +118,27 @@ public class PetDetailFragment extends Fragment {
|
||||
double price = Double.parseDouble(binding.etPetPrice.getText().toString().trim());
|
||||
String status = binding.spinnerPetStatus.getSelectedItem().toString();
|
||||
|
||||
// Get selected customer
|
||||
Long customerId = null;
|
||||
int customerPos = binding.spinnerCustomer.getSelectedItemPosition();
|
||||
if (customerPos > 0) { // 0 means no customer for pet
|
||||
customerId = viewModel.getCustomerList().getValue().get(customerPos - 1).getId();
|
||||
if (binding.spinnerCustomer.getSelectedItemPosition() > 0) {
|
||||
customerId = viewModel.getCustomerList().getValue().get(binding.spinnerCustomer.getSelectedItemPosition() - 1).getId();
|
||||
}
|
||||
|
||||
// Get selected store
|
||||
Long storeId = null;
|
||||
int storePos = binding.spinnerStore.getSelectedItemPosition();
|
||||
if (storePos > 0) {
|
||||
storeId = viewModel.getStoreList().getValue().get(storePos - 1).getId();
|
||||
if (binding.spinnerStore.getSelectedItemPosition() > 0) {
|
||||
storeId = viewModel.getStoreList().getValue().get(binding.spinnerStore.getSelectedItemPosition() - 1).getId();
|
||||
}
|
||||
|
||||
// Validation: If status is Available, a store must be selected
|
||||
if ("Available".equalsIgnoreCase(status)) {
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerStore, "Store")) return;
|
||||
}
|
||||
|
||||
// Validation: If status is Owned, an owner must be selected
|
||||
if ("Owned".equalsIgnoreCase(status)) {
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerCustomer, "Owner")) return;
|
||||
}
|
||||
|
||||
// Validation: If status is Adopted, an owner and store must be selected
|
||||
if ("Adopted".equalsIgnoreCase(status)) {
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerCustomer, "Owner")) return;
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerStore, "Store")) return;
|
||||
}
|
||||
|
||||
//create a pet object to send to the API
|
||||
PetDTO petDTO = new PetDTO();
|
||||
petDTO.setPetName(name);
|
||||
petDTO.setPetSpecies(species);
|
||||
@@ -155,6 +150,8 @@ public class PetDetailFragment extends Fragment {
|
||||
petDTO.setStoreId(storeId);
|
||||
|
||||
viewModel.savePet(petDTO).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
if (viewModel.isEditing()) {
|
||||
ActivityLogger.logChange(requireContext(), "Pet", "UPDATED", (int) viewModel.getPetId());
|
||||
@@ -170,12 +167,11 @@ public class PetDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirmation dialog and handles the deletion of a pet.
|
||||
*/
|
||||
private void deletePet() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Pet", () ->
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Pet", () -> {
|
||||
viewModel.deletePet().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
ActivityLogger.logChange(requireContext(), "Pet", "DELETED", (int) viewModel.getPetId());
|
||||
Toast.makeText(getContext(), "Pet deleted successfully!", Toast.LENGTH_SHORT).show();
|
||||
@@ -183,32 +179,24 @@ public class PetDetailFragment extends Fragment {
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Delete failed: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the pet list screen.
|
||||
*/
|
||||
private void navigateToPetList() {
|
||||
NavHostFragment.findNavController(this).popBackStack(R.id.nav_pet, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates back to the previous screen.
|
||||
*/
|
||||
private void navigateBack() {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles arguments passed to the fragment to determine if it's in edit or add mode.
|
||||
*/
|
||||
private void handleArguments() {
|
||||
if (getArguments() != null && getArguments().containsKey("petId")) {
|
||||
long petId = getArguments().getLong("petId");
|
||||
viewModel.setPetId(petId);
|
||||
binding.tvMode.setText("Edit Pet");
|
||||
binding.tvPetId.setText("ID: " + petId);
|
||||
binding.tvPetId.setText(DateTimeUtils.formatId(petId));
|
||||
binding.tvPetId.setVisibility(View.VISIBLE);
|
||||
binding.btnDeletePet.setVisibility(View.VISIBLE);
|
||||
|
||||
@@ -225,12 +213,10 @@ public class PetDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches specific pet details from the backend using the ID.
|
||||
*/
|
||||
private void loadPetData() {
|
||||
viewModel.loadPet().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
PetDTO p = resource.data;
|
||||
binding.etPetName.setText(p.getPetName());
|
||||
@@ -253,9 +239,6 @@ public class PetDetailFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the customer spinner with the current list and sets the selection if needed.
|
||||
*/
|
||||
private void updateCustomerSpinnerSelection() {
|
||||
SpinnerUtils.populateSpinner(
|
||||
requireContext(),
|
||||
@@ -268,9 +251,6 @@ public class PetDetailFragment extends Fragment {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the store spinner with the current list and sets the selection if needed.
|
||||
*/
|
||||
private void updateStoreSpinnerSelection() {
|
||||
SpinnerUtils.populateSpinner(
|
||||
requireContext(),
|
||||
@@ -283,9 +263,6 @@ public class PetDetailFragment extends Fragment {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the spinner for pet status selection.
|
||||
*/
|
||||
private void setupSpinner() {
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerPetStatus,
|
||||
new String[]{"Available", "Adopted", "Owned"});
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.example.petstoremobile.api.auth.TokenManager;
|
||||
import com.example.petstoremobile.databinding.FragmentProductDetailBinding;
|
||||
import com.example.petstoremobile.dtos.*;
|
||||
import com.example.petstoremobile.viewmodels.ProductDetailViewModel;
|
||||
import com.example.petstoremobile.utils.DateTimeUtils;
|
||||
import com.example.petstoremobile.utils.DialogUtils;
|
||||
import com.example.petstoremobile.utils.FileUtils;
|
||||
import com.example.petstoremobile.utils.GlideUtils;
|
||||
@@ -111,12 +112,20 @@ public class ProductDetailFragment extends Fragment {
|
||||
viewModel.getCategoryList().observe(getViewLifecycleOwner(), list -> updateCategorySpinner());
|
||||
|
||||
viewModel.loadCategories().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
viewModel.setCategoryList(resource.data.getContent());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCategorySpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerProductCategory, viewModel.getCategoryList().getValue(),
|
||||
CategoryDTO::getCategoryName, "-- Select Category --",
|
||||
@@ -135,7 +144,7 @@ public class ProductDetailFragment extends Fragment {
|
||||
long prodId = a.getLong("prodId");
|
||||
viewModel.setProdId(prodId);
|
||||
binding.tvProductMode.setText("Edit Product");
|
||||
binding.tvProductId.setText("ID: " + prodId);
|
||||
binding.tvProductId.setText(DateTimeUtils.formatId(prodId));
|
||||
binding.tvProductId.setVisibility(View.VISIBLE);
|
||||
binding.btnDeleteProduct.setVisibility(View.VISIBLE);
|
||||
loadProductData();
|
||||
@@ -152,6 +161,7 @@ public class ProductDetailFragment extends Fragment {
|
||||
private void loadProductData() {
|
||||
viewModel.loadProduct().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
ProductDTO p = resource.data;
|
||||
binding.etProductName.setText(p.getProdName());
|
||||
@@ -185,7 +195,9 @@ public class ProductDetailFragment extends Fragment {
|
||||
private void performPendingImageActions(String successMsg) {
|
||||
if (isImageRemoved) {
|
||||
viewModel.deleteProductImage().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status != Resource.Status.LOADING) {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status != Resource.Status.LOADING) {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), successMsg, Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
@@ -214,7 +226,9 @@ public class ProductDetailFragment extends Fragment {
|
||||
MultipartBody.Part body = MultipartBody.Part.createFormData("image", file.getName(), requestFile);
|
||||
|
||||
viewModel.uploadProductImage(body).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status != Resource.Status.LOADING) {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status != Resource.Status.LOADING) {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), successMsg, Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
@@ -227,15 +241,8 @@ public class ProductDetailFragment extends Fragment {
|
||||
|
||||
private void saveProduct() {
|
||||
if (!InputValidator.isNotEmpty(binding.etProductName, "Product Name")) return;
|
||||
|
||||
if (binding.spinnerProductCategory.getSelectedItemPosition() == 0) {
|
||||
Toast.makeText(getContext(), "Select a category", Toast.LENGTH_SHORT).show(); return;
|
||||
}
|
||||
|
||||
if (!InputValidator.isNotEmpty(binding.etProductPrice, "Price") ||
|
||||
!InputValidator.isPositiveDecimal(binding.etProductPrice, "Price")) {
|
||||
return;
|
||||
}
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerProductCategory, "Category")) return;
|
||||
if (!InputValidator.isPositiveDecimal(binding.etProductPrice, "Price")) return;
|
||||
|
||||
String name = binding.etProductName.getText().toString().trim();
|
||||
String desc = binding.etProductDesc.getText().toString().trim();
|
||||
@@ -245,7 +252,9 @@ public class ProductDetailFragment extends Fragment {
|
||||
ProductDTO dto = new ProductDTO(name, category.getCategoryId(), desc, price);
|
||||
|
||||
viewModel.saveProduct(dto).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status != Resource.Status.LOADING) {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status != Resource.Status.LOADING) {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
if (resource.data != null) {
|
||||
viewModel.setProdId(resource.data.getProdId());
|
||||
@@ -261,9 +270,11 @@ public class ProductDetailFragment extends Fragment {
|
||||
private void confirmDelete() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Product", () ->
|
||||
viewModel.deleteProduct().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS) {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
navigateBack();
|
||||
} else if (resource != null && resource.status == Resource.Status.ERROR) {
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Delete failed: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.example.petstoremobile.utils.DialogUtils;
|
||||
import com.example.petstoremobile.utils.InputValidator;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.SpinnerUtils;
|
||||
import com.example.petstoremobile.utils.UIUtils;
|
||||
import com.example.petstoremobile.viewmodels.ProductSupplierDetailViewModel;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@@ -64,6 +65,12 @@ public class ProductSupplierDetailFragment extends Fragment {
|
||||
viewModel.getSupplierList().observe(getViewLifecycleOwner(), list -> refreshSupplierSpinner());
|
||||
}
|
||||
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
@@ -72,11 +79,15 @@ public class ProductSupplierDetailFragment extends Fragment {
|
||||
|
||||
private void loadSpinnersData() {
|
||||
viewModel.loadProducts().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
viewModel.setProductList(resource.data.getContent());
|
||||
}
|
||||
});
|
||||
viewModel.loadSuppliers().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
viewModel.setSupplierList(resource.data.getContent());
|
||||
}
|
||||
@@ -113,17 +124,9 @@ public class ProductSupplierDetailFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void save() {
|
||||
if (binding.spinnerPSProduct.getSelectedItemPosition() == 0) {
|
||||
Toast.makeText(getContext(), "Select a product", Toast.LENGTH_SHORT).show(); return;
|
||||
}
|
||||
if (binding.spinnerPSSupplier.getSelectedItemPosition() == 0) {
|
||||
Toast.makeText(getContext(), "Select a supplier", Toast.LENGTH_SHORT).show(); return;
|
||||
}
|
||||
|
||||
if (!InputValidator.isNotEmpty(binding.etPSCost, "Cost") ||
|
||||
!InputValidator.isPositiveDecimal(binding.etPSCost, "Cost")) {
|
||||
return;
|
||||
}
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerPSProduct, "Product")) return;
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerPSSupplier, "Supplier")) return;
|
||||
if (!InputValidator.isPositiveDecimal(binding.etPSCost, "Cost")) return;
|
||||
|
||||
ProductDTO product = viewModel.getProductList().getValue().get(binding.spinnerPSProduct.getSelectedItemPosition() - 1);
|
||||
SupplierDTO supplier = viewModel.getSupplierList().getValue().get(binding.spinnerPSSupplier.getSelectedItemPosition() - 1);
|
||||
@@ -132,6 +135,8 @@ public class ProductSupplierDetailFragment extends Fragment {
|
||||
ProductSupplierDTO dto = new ProductSupplierDTO(product.getProdId(), supplier.getSupId(), cost);
|
||||
|
||||
viewModel.saveProductSupplier(dto).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), viewModel.isEditing() ? "Updated" : "Saved", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
@@ -142,8 +147,10 @@ public class ProductSupplierDetailFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void confirmDelete() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Product Supplier", () ->
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Product Supplier Relationship", () ->
|
||||
viewModel.deleteProductSupplier().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), "Deleted", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
|
||||
@@ -66,9 +66,16 @@ public class PurchaseOrderDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadPurchaseOrderData() {
|
||||
viewModel.loadPurchaseOrder(purchaseOrderId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
PurchaseOrderDTO po = resource.data;
|
||||
binding.tvPODetailId.setText("PO #" + po.getPurchaseOrderId());
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.*;
|
||||
@@ -13,7 +12,9 @@ import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.databinding.FragmentRefundBinding;
|
||||
import com.example.petstoremobile.dtos.SaleDTO;
|
||||
import com.example.petstoremobile.viewmodels.RefundViewModel;
|
||||
import com.example.petstoremobile.utils.DialogUtils;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.SpinnerUtils;
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
@@ -50,8 +51,7 @@ public class RefundFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void setupSpinner() {
|
||||
binding.spinnerRefundPayment.setAdapter(new ArrayAdapter<>(requireContext(),
|
||||
android.R.layout.simple_spinner_item, PAYMENT_METHODS));
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerRefundPayment, PAYMENT_METHODS);
|
||||
}
|
||||
|
||||
private void observeViewModel() {
|
||||
@@ -63,9 +63,17 @@ public class RefundFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadAllSales() {
|
||||
viewModel.loadAllSales().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
viewModel.setAllSales(resource.data.getContent());
|
||||
Bundle args = getArguments();
|
||||
if (args != null && args.containsKey("saleId")) {
|
||||
@@ -122,11 +130,7 @@ public class RefundFragment extends Fragment {
|
||||
+ " | Payment: " + currentSale.getPaymentMethod());
|
||||
|
||||
if (currentSale.getPaymentMethod() != null) {
|
||||
for (int i = 0; i < PAYMENT_METHODS.length; i++) {
|
||||
if (PAYMENT_METHODS[i].equalsIgnoreCase(currentSale.getPaymentMethod())) {
|
||||
binding.spinnerRefundPayment.setSelection(i); break;
|
||||
}
|
||||
}
|
||||
SpinnerUtils.setSelectionByValue(binding.spinnerRefundPayment, currentSale.getPaymentMethod());
|
||||
}
|
||||
|
||||
if (viewModel.getAvailableItems().getValue() == null || viewModel.getAvailableItems().getValue().isEmpty()) {
|
||||
@@ -263,17 +267,17 @@ public class RefundFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void showQuantityDialog(RefundViewModel.RefundItem item, int available) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(requireContext());
|
||||
builder.setTitle("Refund Quantity");
|
||||
builder.setMessage("Product: " + item.productName + "\nAvailable: " + available);
|
||||
|
||||
EditText input = new EditText(getContext());
|
||||
input.setInputType(android.text.InputType.TYPE_CLASS_NUMBER);
|
||||
input.setText(String.valueOf(available));
|
||||
input.setSelectAllOnFocus(true);
|
||||
builder.setView(input);
|
||||
input.setPadding(40, 40, 40, 40);
|
||||
|
||||
builder.setPositiveButton("Add to Refund", (d, w) -> {
|
||||
new androidx.appcompat.app.AlertDialog.Builder(requireContext())
|
||||
.setTitle("Refund Quantity")
|
||||
.setMessage("Product: " + item.productName + "\nAvailable: " + available)
|
||||
.setView(input)
|
||||
.setPositiveButton("Add to Refund", (d, w) -> {
|
||||
String val = input.getText().toString().trim();
|
||||
if (val.isEmpty()) return;
|
||||
int qty;
|
||||
@@ -290,12 +294,10 @@ public class RefundFragment extends Fragment {
|
||||
Toast.makeText(getContext(), "Cannot exceed " + available, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
viewModel.addToCart(item, qty);
|
||||
});
|
||||
|
||||
builder.setNegativeButton("Cancel", null);
|
||||
builder.show();
|
||||
})
|
||||
.setNegativeButton("Cancel", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void updateRefundTotal() {
|
||||
@@ -322,18 +324,16 @@ public class RefundFragment extends Fragment {
|
||||
for (RefundViewModel.RefundItem item : viewModel.getRefundCart().getValue()) total = total.add(item.getTotal());
|
||||
final BigDecimal finalTotal = total;
|
||||
|
||||
new AlertDialog.Builder(requireContext())
|
||||
.setTitle("Confirm Refund")
|
||||
.setMessage("Process refund for Sale #" + viewModel.getCurrentSale().getSaleId()
|
||||
+ "?\nRefund amount: $" + finalTotal.setScale(2, RoundingMode.HALF_UP))
|
||||
.setPositiveButton("Yes", (d, w) -> submitRefund(payment))
|
||||
.setNegativeButton("No", null)
|
||||
.show();
|
||||
DialogUtils.showConfirmDialog(requireContext(), "Confirm Refund",
|
||||
"Process refund for Sale #" + viewModel.getCurrentSale().getSaleId()
|
||||
+ "?\nRefund amount: $" + finalTotal.setScale(2, RoundingMode.HALF_UP),
|
||||
() -> submitRefund(payment));
|
||||
}
|
||||
|
||||
private void submitRefund(String payment) {
|
||||
viewModel.submitRefund(payment).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null) {
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), "Refund processed successfully!", Toast.LENGTH_LONG).show();
|
||||
navigateBack();
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.example.petstoremobile.dtos.*;
|
||||
import com.example.petstoremobile.viewmodels.SaleDetailViewModel;
|
||||
import com.example.petstoremobile.utils.SpinnerUtils;
|
||||
import com.example.petstoremobile.utils.DialogUtils;
|
||||
import com.example.petstoremobile.utils.InputValidator;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.UIUtils;
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
@@ -107,21 +108,35 @@ public class SaleDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadData() {
|
||||
viewModel.loadStores().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) viewModel.setStoreList(resource.data);
|
||||
});
|
||||
viewModel.loadCustomers().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) viewModel.setCustomerList(resource.data);
|
||||
});
|
||||
viewModel.loadProducts().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) viewModel.setProductList(resource.data.getContent());
|
||||
});
|
||||
}
|
||||
|
||||
private void loadSaleDetails() {
|
||||
viewModel.loadSaleDetails().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
SaleDTO sale = resource.data;
|
||||
binding.tvSaleDetailTotal.setText("Total: $" + sale.getTotalAmount());
|
||||
binding.tvSaleSubtotal.setText("$" + (sale.getSubtotalAmount() != null ? sale.getSubtotalAmount() : sale.getTotalAmount()));
|
||||
@@ -157,22 +172,10 @@ public class SaleDetailFragment extends Fragment {
|
||||
|
||||
private void setupAddItem() {
|
||||
binding.btnAddItem.setOnClickListener(v -> {
|
||||
if (binding.spinnerSaleProduct.getSelectedItemPosition() == 0) {
|
||||
Toast.makeText(getContext(), "Select a product", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
String qtyStr = binding.etSaleQuantity.getText().toString().trim();
|
||||
if (qtyStr.isEmpty()) {
|
||||
binding.etSaleQuantity.setError("Enter quantity");
|
||||
return;
|
||||
}
|
||||
int qty;
|
||||
try { qty = Integer.parseInt(qtyStr); }
|
||||
catch (Exception e) {
|
||||
binding.etSaleQuantity.setError("Invalid quantity");
|
||||
return;
|
||||
}
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerSaleProduct, "Product")) return;
|
||||
if (!InputValidator.isPositiveInteger(binding.etSaleQuantity, "Quantity")) return;
|
||||
|
||||
int qty = Integer.parseInt(binding.etSaleQuantity.getText().toString().trim());
|
||||
ProductDTO product = viewModel.getProductList().getValue().get(binding.spinnerSaleProduct.getSelectedItemPosition() - 1);
|
||||
|
||||
for (SaleDTO.SaleItemDTO existing : viewModel.getCartItems().getValue()) {
|
||||
@@ -238,10 +241,8 @@ public class SaleDetailFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void saveSale() {
|
||||
if (binding.spinnerSaleStore.getSelectedItemPosition() == 0) {
|
||||
Toast.makeText(getContext(), "Select a store", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
if (!InputValidator.isSpinnerSelected(binding.spinnerSaleStore, "Store")) return;
|
||||
|
||||
if (viewModel.getCartItems().getValue() == null || viewModel.getCartItems().getValue().isEmpty()) {
|
||||
Toast.makeText(getContext(), "Add at least one item", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
@@ -259,6 +260,7 @@ public class SaleDetailFragment extends Fragment {
|
||||
|
||||
viewModel.createSale(dto).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null) {
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), "Sale saved!", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.databinding.FragmentServiceDetailBinding;
|
||||
import com.example.petstoremobile.dtos.ServiceDTO;
|
||||
import com.example.petstoremobile.utils.ActivityLogger;
|
||||
import com.example.petstoremobile.utils.DateTimeUtils;
|
||||
import com.example.petstoremobile.utils.DialogUtils;
|
||||
import com.example.petstoremobile.utils.InputValidator;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
@@ -57,6 +58,12 @@ public class ServiceDetailFragment extends Fragment {
|
||||
binding.btnDeleteService.setOnClickListener(v -> deleteService());
|
||||
}
|
||||
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
@@ -81,6 +88,8 @@ public class ServiceDetailFragment extends Fragment {
|
||||
serviceDTO.setServicePrice(price);
|
||||
|
||||
viewModel.saveService(serviceDTO).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
if (viewModel.isEditing()) {
|
||||
ActivityLogger.logChange(requireContext(), "Service", "UPDATED", (int) viewModel.getServiceId());
|
||||
@@ -99,6 +108,8 @@ public class ServiceDetailFragment extends Fragment {
|
||||
private void deleteService() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Service", () ->
|
||||
viewModel.deleteService().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
ActivityLogger.logChange(requireContext(), "Service", "DELETED", (int) viewModel.getServiceId());
|
||||
Toast.makeText(getContext(), "Service deleted successfully!", Toast.LENGTH_SHORT).show();
|
||||
@@ -118,7 +129,7 @@ public class ServiceDetailFragment extends Fragment {
|
||||
long serviceId = getArguments().getLong("serviceId");
|
||||
viewModel.setServiceId(serviceId);
|
||||
binding.tvMode.setText("Edit Service");
|
||||
binding.tvServiceId.setText("ID: " + serviceId);
|
||||
binding.tvServiceId.setText(DateTimeUtils.formatId(serviceId));
|
||||
binding.btnDeleteService.setVisibility(View.VISIBLE);
|
||||
loadServiceData();
|
||||
} else {
|
||||
@@ -133,6 +144,7 @@ public class ServiceDetailFragment extends Fragment {
|
||||
private void loadServiceData() {
|
||||
viewModel.loadService().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
ServiceDTO s = resource.data;
|
||||
binding.etServiceName.setText(s.getServiceName());
|
||||
|
||||
@@ -4,13 +4,16 @@ import android.os.Bundle;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.databinding.FragmentStaffDetailBinding;
|
||||
import com.example.petstoremobile.dtos.EmployeeDTO;
|
||||
import com.example.petstoremobile.utils.DialogUtils;
|
||||
import com.example.petstoremobile.utils.InputValidator;
|
||||
import com.example.petstoremobile.utils.SpinnerUtils;
|
||||
import com.example.petstoremobile.utils.UIUtils;
|
||||
import com.example.petstoremobile.viewmodels.StaffDetailViewModel;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
@@ -36,14 +39,15 @@ public class StaffDetailFragment extends Fragment {
|
||||
binding.btnStaffBack.setOnClickListener(v -> navigateBack());
|
||||
binding.btnSaveStaff.setOnClickListener(v -> save());
|
||||
binding.btnDeleteStaff.setOnClickListener(v -> confirmDelete());
|
||||
|
||||
UIUtils.formatPhoneInput(binding.etStaffPhone);
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
private void setupSpinners() {
|
||||
binding.spinnerStaffRole.setAdapter(new ArrayAdapter<>(requireContext(),
|
||||
android.R.layout.simple_spinner_item, ROLES));
|
||||
binding.spinnerStaffStatus.setAdapter(new ArrayAdapter<>(requireContext(),
|
||||
android.R.layout.simple_spinner_item, STATUSES));
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerStaffRole, ROLES);
|
||||
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerStaffStatus, STATUSES);
|
||||
}
|
||||
|
||||
private void handleArguments() {
|
||||
@@ -62,16 +66,8 @@ public class StaffDetailFragment extends Fragment {
|
||||
binding.etStaffPhone.setText(a.getString("phone", ""));
|
||||
binding.btnDeleteStaff.setVisibility(View.VISIBLE);
|
||||
|
||||
String role = a.getString("role", "STAFF");
|
||||
for (int i = 0; i < ROLES.length; i++) {
|
||||
if (ROLES[i].equals(role)) {
|
||||
binding.spinnerStaffRole.setSelection(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
boolean active = a.getBoolean("active", true);
|
||||
binding.spinnerStaffStatus.setSelection(active ? 0 : 1);
|
||||
SpinnerUtils.setSelectionByValue(binding.spinnerStaffRole, a.getString("role", "STAFF"));
|
||||
binding.spinnerStaffStatus.setSelection(a.getBoolean("active", true) ? 0 : 1);
|
||||
|
||||
} else {
|
||||
viewModel.setEmployeeId(-1, false);
|
||||
@@ -81,29 +77,39 @@ public class StaffDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void save() {
|
||||
String username = binding.etStaffUsername.getText() != null ? binding.etStaffUsername.getText().toString().trim() : "";
|
||||
String password = binding.etStaffPassword.getText() != null ? binding.etStaffPassword.getText().toString().trim() : "";
|
||||
String firstName = binding.etStaffFirstName.getText() != null ? binding.etStaffFirstName.getText().toString().trim() : "";
|
||||
String lastName = binding.etStaffLastName.getText() != null ? binding.etStaffLastName.getText().toString().trim() : "";
|
||||
String email = binding.etStaffEmail.getText() != null ? binding.etStaffEmail.getText().toString().trim() : "";
|
||||
String phone = binding.etStaffPhone.getText() != null ? binding.etStaffPhone.getText().toString().trim() : "";
|
||||
if (!InputValidator.isNotEmpty(binding.etStaffUsername, "Username")) return;
|
||||
|
||||
if (!viewModel.isEditing()) {
|
||||
if (!InputValidator.isNotEmpty(binding.etStaffPassword, "Password")) return;
|
||||
String pass = binding.etStaffPassword.getText().toString();
|
||||
if (pass.length() < 6) {
|
||||
binding.etStaffPassword.setError("At least 6 characters");
|
||||
binding.etStaffPassword.requestFocus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!InputValidator.isNotEmpty(binding.etStaffFirstName, "First Name")) return;
|
||||
if (!InputValidator.isNotEmpty(binding.etStaffLastName, "Last Name")) return;
|
||||
if (!InputValidator.isValidEmail(binding.etStaffEmail)) return;
|
||||
if (!InputValidator.isValidPhone(binding.etStaffPhone)) return;
|
||||
|
||||
String username = binding.etStaffUsername.getText().toString().trim();
|
||||
String password = binding.etStaffPassword.getText().toString().trim();
|
||||
String firstName = binding.etStaffFirstName.getText().toString().trim();
|
||||
String lastName = binding.etStaffLastName.getText().toString().trim();
|
||||
String email = binding.etStaffEmail.getText().toString().trim();
|
||||
String phone = binding.etStaffPhone.getText().toString().trim();
|
||||
String role = ROLES[binding.spinnerStaffRole.getSelectedItemPosition()];
|
||||
boolean active = binding.spinnerStaffStatus.getSelectedItemPosition() == 0;
|
||||
|
||||
if (username.isEmpty()) { binding.etStaffUsername.setError("Required"); return; }
|
||||
if (!viewModel.isEditing() && password.isEmpty()) {
|
||||
binding.etStaffPassword.setError("Required for new account"); return;
|
||||
}
|
||||
if (!viewModel.isEditing() && password.length() < 6) {
|
||||
binding.etStaffPassword.setError("At least 6 characters"); return;
|
||||
}
|
||||
if (firstName.isEmpty()) { binding.etStaffFirstName.setError("Required"); return; }
|
||||
if (lastName.isEmpty()) { binding.etStaffLastName.setError("Required"); return; }
|
||||
if (email.isEmpty()) { binding.etStaffEmail.setError("Required"); return; }
|
||||
if (phone.isEmpty()) { binding.etStaffPhone.setError("Required"); return; }
|
||||
|
||||
EmployeeDTO dto = new EmployeeDTO(
|
||||
username,
|
||||
password.isEmpty() ? null : password,
|
||||
@@ -117,6 +123,7 @@ public class StaffDetailFragment extends Fragment {
|
||||
|
||||
viewModel.saveEmployee(dto).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null) {
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), viewModel.isEditing() ? "Updated successfully" : "Staff account created", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
@@ -128,12 +135,10 @@ public class StaffDetailFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void confirmDelete() {
|
||||
new AlertDialog.Builder(requireContext())
|
||||
.setTitle("Delete Staff Account?")
|
||||
.setMessage("This will permanently delete this staff account.")
|
||||
.setPositiveButton("Yes", (d, w) ->
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Staff Account", () ->
|
||||
viewModel.deleteEmployee().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null) {
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
navigateBack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
@@ -141,8 +146,7 @@ public class StaffDetailFragment extends Fragment {
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}))
|
||||
.setNegativeButton("No", null).show();
|
||||
}));
|
||||
}
|
||||
|
||||
private void navigateBack() {
|
||||
|
||||
@@ -58,6 +58,12 @@ public class SupplierDetailFragment extends Fragment {
|
||||
binding.btnDeleteSupplier.setOnClickListener(v -> deleteSupplier());
|
||||
}
|
||||
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
@@ -85,6 +91,8 @@ public class SupplierDetailFragment extends Fragment {
|
||||
supplierDTO.setSupPhone(phone);
|
||||
|
||||
viewModel.saveSupplier(supplierDTO).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
if (viewModel.isEditing()) {
|
||||
ActivityLogger.logChange(requireContext(), "Supplier", "UPDATED", (int) viewModel.getSupId());
|
||||
@@ -103,6 +111,8 @@ public class SupplierDetailFragment extends Fragment {
|
||||
private void deleteSupplier() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Supplier", () ->
|
||||
viewModel.deleteSupplier().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
ActivityLogger.logChange(requireContext(), "Supplier", "DELETED", (int) viewModel.getSupId());
|
||||
Toast.makeText(getContext(), "Supplier deleted successfully!", Toast.LENGTH_SHORT).show();
|
||||
@@ -138,6 +148,7 @@ public class SupplierDetailFragment extends Fragment {
|
||||
private void loadSupplierData() {
|
||||
viewModel.loadSupplier().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
SupplierDTO s = resource.data;
|
||||
binding.etSupCompany.setText(s.getSupCompany());
|
||||
|
||||
@@ -95,6 +95,12 @@ public class PetProfileFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
private void setLoading(boolean loading) {
|
||||
if (binding != null && binding.progressBar != null) {
|
||||
binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
@@ -104,6 +110,7 @@ public class PetProfileFragment extends Fragment {
|
||||
private void loadPetData() {
|
||||
viewModel.getPetById(petId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
PetDTO pet = resource.data;
|
||||
binding.tvPetName.setText(pet.getPetName());
|
||||
@@ -172,14 +179,14 @@ public class PetProfileFragment extends Fragment {
|
||||
MultipartBody.Part body = MultipartBody.Part.createFormData("image", file.getName(), requestFile);
|
||||
|
||||
viewModel.uploadPetImage(petId, body).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status != Resource.Status.LOADING) {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), "Pet photo updated successfully", Toast.LENGTH_SHORT).show();
|
||||
loadPetImage((int) petId);
|
||||
} else {
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Upload failed: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Log.e("UPLOAD_PET_IMAGE", "Error: " + e.getMessage());
|
||||
@@ -188,15 +195,15 @@ public class PetProfileFragment extends Fragment {
|
||||
|
||||
private void deletePetImage() {
|
||||
viewModel.deletePetImage(petId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status != Resource.Status.LOADING) {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), "Pet photo removed", Toast.LENGTH_SHORT).show();
|
||||
hasImage = false;
|
||||
binding.imgPet.setImageResource(R.drawable.placeholder);
|
||||
} else {
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Delete failed: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,12 +61,15 @@ public class ChatListViewModel extends ViewModel {
|
||||
}
|
||||
|
||||
public void loadCustomers() {
|
||||
isLoading.setValue(true);
|
||||
customerRepository.getAllCustomers(0, 100).observeForever(resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
for (CustomerDTO c : resource.data.getContent()) {
|
||||
customerNames.put(c.getCustomerId(), c.getFullName());
|
||||
}
|
||||
loadConversations();
|
||||
} else if (resource != null && resource.status == Resource.Status.ERROR) {
|
||||
isLoading.setValue(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -96,6 +99,7 @@ public class ChatListViewModel extends ViewModel {
|
||||
}
|
||||
|
||||
public void loadMessageHistory(Long conversationId) {
|
||||
isLoading.setValue(true);
|
||||
chatRepository.getMessages(conversationId).observeForever(resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
List<Message> messages = new ArrayList<>();
|
||||
@@ -103,6 +107,9 @@ public class ChatListViewModel extends ViewModel {
|
||||
messages.add(dtoToModel(dto));
|
||||
}
|
||||
messageList.setValue(messages);
|
||||
isLoading.setValue(false);
|
||||
} else if (resource != null && resource.status == Resource.Status.ERROR) {
|
||||
isLoading.setValue(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ public class PetDetailViewModel extends ViewModel {
|
||||
private final MutableLiveData<PetDTO> petState = new MutableLiveData<>();
|
||||
private final MutableLiveData<List<DropdownDTO>> customerList = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<List<DropdownDTO>> storeList = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<Boolean> isLoading = new MutableLiveData<>(false);
|
||||
|
||||
private long petId = -1;
|
||||
private boolean isEditing = false;
|
||||
@@ -91,4 +92,12 @@ public class PetDetailViewModel extends ViewModel {
|
||||
public LiveData<List<DropdownDTO>> getStoreList() {
|
||||
return storeList;
|
||||
}
|
||||
|
||||
public LiveData<Boolean> getIsLoading() {
|
||||
return isLoading;
|
||||
}
|
||||
|
||||
public void setLoading(boolean loading) {
|
||||
isLoading.setValue(loading);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ public class SaleDetailViewModel extends ViewModel {
|
||||
private final MutableLiveData<List<DropdownDTO>> customerList = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<List<ProductDTO>> productList = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<List<SaleDTO.SaleItemDTO>> cartItems = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<Boolean> isLoading = new MutableLiveData<>(false);
|
||||
|
||||
@Inject
|
||||
public SaleDetailViewModel(SaleRepository saleRepository, StoreRepository storeRepository,
|
||||
@@ -106,4 +107,12 @@ public class SaleDetailViewModel extends ViewModel {
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
public LiveData<Boolean> getIsLoading() {
|
||||
return isLoading;
|
||||
}
|
||||
|
||||
public void setLoading(boolean loading) {
|
||||
isLoading.setValue(loading);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
@@ -212,3 +216,13 @@
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
android:indeterminateTint="@color/accent_coral"/>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -1,5 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
@@ -316,3 +320,13 @@
|
||||
</ScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
android:indeterminateTint="@color/accent_coral"/>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -1,5 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
@@ -266,3 +270,13 @@
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
android:indeterminateTint="@color/accent_coral"/>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -6,11 +6,15 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/background_grey">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/background_grey">
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
@@ -126,6 +130,16 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:visibility="gone"
|
||||
android:indeterminateTint="@color/accent_coral"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/chatListDrawer"
|
||||
android:layout_width="260dp"
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
@@ -153,3 +157,13 @@
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
android:indeterminateTint="@color/accent_coral"/>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -1,5 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
@@ -227,3 +232,13 @@
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
android:indeterminateTint="@color/accent_coral"/>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -1,5 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
@@ -304,3 +308,13 @@
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
android:indeterminateTint="@color/accent_coral"/>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -1,5 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
@@ -193,3 +197,13 @@
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
android:indeterminateTint="@color/accent_coral"/>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -1,5 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
@@ -136,3 +140,13 @@
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
android:indeterminateTint="@color/accent_coral"/>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -1,5 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/background_grey">
|
||||
@@ -198,3 +202,13 @@
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
android:indeterminateTint="@color/accent_coral"/>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -1,5 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
@@ -138,3 +142,13 @@
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
android:indeterminateTint="@color/accent_coral"/>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -1,5 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
@@ -220,3 +224,13 @@
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
android:indeterminateTint="@color/accent_coral"/>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -1,5 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
@@ -331,3 +335,13 @@
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
android:indeterminateTint="@color/accent_coral"/>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -1,5 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
@@ -169,3 +173,13 @@
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
android:indeterminateTint="@color/accent_coral"/>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -1,5 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
@@ -231,3 +235,13 @@
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
android:indeterminateTint="@color/accent_coral"/>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -1,5 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
@@ -180,3 +184,13 @@
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
android:indeterminateTint="@color/accent_coral"/>
|
||||
|
||||
</FrameLayout>
|
||||
Reference in New Issue
Block a user