From 4b9bf4dff4be917d3d166f0319370ffe75c213bd Mon Sep 17 00:00:00 2001 From: Alex <78383757+Lextical@users.noreply.github.com> Date: Sun, 5 Apr 2026 22:50:25 -0600 Subject: [PATCH] Implemented View Binding to reduce code - project uses view binding now so we don have to do getViewbyId to refer to the xml --- android/app/build.gradle.kts | 1 + .../activities/HomeActivity.java | 18 ++- .../activities/MainActivity.java | 48 +++----- .../fragments/ChatFragment.java | 101 ++++++--------- .../fragments/ListFragment.java | 76 +++++------- .../fragments/ProfileFragment.java | 64 ++++------ .../listfragments/AdoptionFragment.java | 95 +++++++-------- .../listfragments/AppointmentFragment.java | 115 ++++++------------ .../listfragments/InventoryFragment.java | 94 ++++++-------- .../fragments/listfragments/PetFragment.java | 79 +++++------- .../listfragments/ProductFragment.java | 62 +++++----- .../ProductSupplierFragment.java | 57 +++++---- .../listfragments/PurchaseOrderFragment.java | 55 +++++---- .../fragments/listfragments/SaleFragment.java | 67 +++++----- .../listfragments/ServiceFragment.java | 59 ++++----- .../listfragments/SupplierFragment.java | 59 ++++----- .../AdoptionDetailFragment.java | 76 +++++------- .../AppointmentDetailFragment.java | 104 +++++++--------- .../InventoryDetailFragment.java | 104 +++++++--------- .../detailfragments/PetDetailFragment.java | 97 ++++++--------- .../ProductDetailFragment.java | 84 ++++++------- .../ProductSupplierDetailFragment.java | 61 ++++------ .../PurchaseOrderDetailFragment.java | 40 +++--- .../detailfragments/RefundDetailFragment.java | 69 +++++------ .../ServiceDetailFragment.java | 78 +++++------- .../SupplierDetailFragment.java | 94 ++++++-------- 26 files changed, 773 insertions(+), 1084 deletions(-) diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 87add966..6cd974d9 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -39,6 +39,7 @@ android { buildFeatures { buildConfig = true + viewBinding = true } buildTypes { diff --git a/android/app/src/main/java/com/example/petstoremobile/activities/HomeActivity.java b/android/app/src/main/java/com/example/petstoremobile/activities/HomeActivity.java index d9e41f96..01b4ca24 100644 --- a/android/app/src/main/java/com/example/petstoremobile/activities/HomeActivity.java +++ b/android/app/src/main/java/com/example/petstoremobile/activities/HomeActivity.java @@ -20,14 +20,14 @@ import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.ui.NavigationUI; import com.example.petstoremobile.R; +import com.example.petstoremobile.databinding.ActivityHomeBinding; import com.example.petstoremobile.services.ChatNotificationService; -import com.google.android.material.bottomnavigation.BottomNavigationView; import dagger.hilt.android.AndroidEntryPoint; @AndroidEntryPoint public class HomeActivity extends AppCompatActivity { - private BottomNavigationView bottomNav; + private ActivityHomeBinding binding; private NavController navController; // Launcher to ask for notification permission @@ -45,23 +45,21 @@ public class HomeActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { EdgeToEdge.enable(this); super.onCreate(savedInstanceState); - setContentView(R.layout.activity_home); + binding = ActivityHomeBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); - ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { + ViewCompat.setOnApplyWindowInsetsListener(binding.main, (v, insets) -> { Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); return insets; }); - //get the bottom navbar from the layout - bottomNav = findViewById(R.id.bottom_navigation); - // Initialize Navigation Component NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager() .findFragmentById(R.id.nav_host_fragment); if (navHostFragment != null) { navController = navHostFragment.getNavController(); - NavigationUI.setupWithNavController(bottomNav, navController); + NavigationUI.setupWithNavController(binding.bottomNavigation, navController); } //load the list fragment by default if it's a fresh start @@ -89,9 +87,9 @@ public class HomeActivity extends AppCompatActivity { */ private void handleIntent(Intent intent) { if (intent != null && "chat".equals(intent.getStringExtra("navigate_to"))) { - if (bottomNav != null) { + if (binding.bottomNavigation != null) { // Navigate by selecting the bottom nav item. - bottomNav.setSelectedItemId(R.id.nav_chat); + binding.bottomNavigation.setSelectedItemId(R.id.nav_chat); } } } diff --git a/android/app/src/main/java/com/example/petstoremobile/activities/MainActivity.java b/android/app/src/main/java/com/example/petstoremobile/activities/MainActivity.java index 3d27aab3..f8c89342 100644 --- a/android/app/src/main/java/com/example/petstoremobile/activities/MainActivity.java +++ b/android/app/src/main/java/com/example/petstoremobile/activities/MainActivity.java @@ -2,11 +2,7 @@ package com.example.petstoremobile.activities; import android.content.Intent; import android.os.Bundle; -import android.util.Log; import android.view.inputmethod.EditorInfo; -import android.widget.Button; -import android.widget.EditText; -import android.widget.TextView; import android.widget.Toast; import androidx.activity.EdgeToEdge; @@ -16,8 +12,8 @@ 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.TokenManager; +import com.example.petstoremobile.databinding.ActivityMainBinding; import com.example.petstoremobile.viewmodels.AuthViewModel; import com.example.petstoremobile.utils.Resource; @@ -30,10 +26,7 @@ import dagger.hilt.android.AndroidEntryPoint; @AndroidEntryPoint public class MainActivity extends AppCompatActivity { - private EditText etUser; - private EditText etPassword; - private Button btnLogin; - private TextView tvLoginStatus; + private ActivityMainBinding binding; private AuthViewModel viewModel; @Inject TokenManager tokenManager; @@ -59,41 +52,38 @@ public class MainActivity extends AppCompatActivity { } } - setContentView(R.layout.activity_main); + binding = ActivityMainBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + viewModel = new ViewModelProvider(this).get(AuthViewModel.class); - ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { + ViewCompat.setOnApplyWindowInsetsListener(binding.main, (v, insets) -> { Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); return insets; }); - //get all controls from layout - tvLoginStatus = findViewById(R.id.tvLoginStatus); - etUser = findViewById(R.id.etUser); - etPassword = findViewById(R.id.etPassword); - btnLogin = findViewById(R.id.btnLogin); //clear login status - tvLoginStatus.setText(""); + binding.tvLoginStatus.setText(""); // Set editor action listener for password field to login on when enter is pressed - etPassword.setOnEditorActionListener((v, actionId, event) -> { + binding.etPassword.setOnEditorActionListener((v, actionId, event) -> { if (actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_NULL) { - btnLogin.performClick(); + binding.btnLogin.performClick(); return true; } return false; }); //Set click listener for login button - btnLogin.setOnClickListener(v -> { + binding.btnLogin.setOnClickListener(v -> { //Get username and password from text fields - String username = etUser.getText().toString(); - String password = etPassword.getText().toString(); + String username = binding.etUser.getText().toString(); + String password = binding.etPassword.getText().toString(); //check if fields are empty if (username.isEmpty() || password.isEmpty()) { Toast.makeText(this, "Please enter username and password", Toast.LENGTH_SHORT).show(); - tvLoginStatus.setText("Please enter username and password"); + binding.tvLoginStatus.setText("Please enter username and password"); return; } @@ -110,15 +100,15 @@ public class MainActivity extends AppCompatActivity { switch (resource.status) { case LOADING: - btnLogin.setEnabled(false); - tvLoginStatus.setText("Logging in..."); + binding.btnLogin.setEnabled(false); + binding.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"); + binding.btnLogin.setEnabled(true); + binding.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); @@ -127,8 +117,8 @@ public class MainActivity extends AppCompatActivity { } break; case ERROR: - btnLogin.setEnabled(true); - tvLoginStatus.setText(resource.message); + binding.btnLogin.setEnabled(true); + binding.tvLoginStatus.setText(resource.message); Toast.makeText(this, resource.message, Toast.LENGTH_LONG).show(); break; } diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/ChatFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/ChatFragment.java index 25f4a7f1..81383acc 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/ChatFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/ChatFragment.java @@ -9,15 +9,12 @@ import android.provider.OpenableColumns; import android.util.Log; import android.view.*; import android.view.inputmethod.EditorInfo; -import android.widget.*; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.NonNull; import androidx.core.view.GravityCompat; -import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.Glide; import com.example.petstoremobile.R; import com.example.petstoremobile.adapters.ChatAdapter; @@ -26,10 +23,10 @@ import com.example.petstoremobile.api.auth.TokenManager; import com.example.petstoremobile.api.ChatApi; import com.example.petstoremobile.api.CustomerApi; import com.example.petstoremobile.api.MessageApi; +import com.example.petstoremobile.databinding.FragmentChatBinding; import com.example.petstoremobile.dtos.ConversationDTO; import com.example.petstoremobile.dtos.CustomerDTO; import com.example.petstoremobile.dtos.MessageDTO; -import com.example.petstoremobile.dtos.PageResponse; import com.example.petstoremobile.dtos.SendMessageRequest; import com.example.petstoremobile.models.Chat; import com.example.petstoremobile.models.Message; @@ -44,7 +41,6 @@ import javax.inject.Inject; import javax.inject.Named; import dagger.hilt.android.AndroidEntryPoint; -import retrofit2.*; @AndroidEntryPoint public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickListener, StompChatManager.MessageListener, @@ -52,19 +48,7 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis private static final String TAG = "ChatFragment"; - // View - private DrawerLayout drawerLayout; - private RecyclerView rvChatList, rvMessages; - private EditText etMessage; - private Button btnSend; - private ImageButton btnAttach; - private TextView tvChatTitle; - - // Preview views - private View layoutAttachmentPreview; - private ImageView ivPreview; - private TextView tvPreviewName; - private ImageButton btnRemoveAttachment; + private FragmentChatBinding binding; // Adapters private ChatAdapter chatAdapter; @@ -115,35 +99,21 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_chat, container, false); + binding = FragmentChatBinding.inflate(inflater, container, false); - drawerLayout = view.findViewById(R.id.chatDrawerLayout); - rvChatList = view.findViewById(R.id.rvChatList); - rvMessages = view.findViewById(R.id.rvMessages); - etMessage = view.findViewById(R.id.etMessage); - btnSend = view.findViewById(R.id.btnSend); - btnAttach = view.findViewById(R.id.btnAttach); - tvChatTitle = view.findViewById(R.id.tvChatTitle); - - layoutAttachmentPreview = view.findViewById(R.id.layoutAttachmentPreview); - ivPreview = view.findViewById(R.id.ivPreview); - tvPreviewName = view.findViewById(R.id.tvPreviewName); - btnRemoveAttachment = view.findViewById(R.id.btnRemoveAttachment); - - ImageButton hamburger = view.findViewById(R.id.btnHamburger); - hamburger.setOnClickListener(v -> drawerLayout.openDrawer(GravityCompat.START)); + binding.btnHamburger.setOnClickListener(v -> binding.chatDrawerLayout.openDrawer(GravityCompat.START)); // Set editor action listener for message field to send when enter is pressed - etMessage.setOnEditorActionListener((v, actionId, event) -> { + binding.etMessage.setOnEditorActionListener((v, actionId, event) -> { if (actionId == EditorInfo.IME_ACTION_SEND || actionId == EditorInfo.IME_NULL) { - btnSend.performClick(); + binding.btnSend.performClick(); return true; } return false; }); //When the send button is clicked check if there is an attachment and send using the correct helper function - btnSend.setOnClickListener(v -> { + binding.btnSend.setOnClickListener(v -> { if (pendingAttachmentUri != null) { sendWithAttachment(pendingAttachmentUri); } else { @@ -152,13 +122,13 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis }); //When the attachment button is clicked open the file picker - btnAttach.setOnClickListener(v -> selectAttachment()); - btnRemoveAttachment.setOnClickListener(v -> removeAttachment()); + binding.btnAttach.setOnClickListener(v -> selectAttachment()); + binding.btnRemoveAttachment.setOnClickListener(v -> removeAttachment()); setupRecyclerViews(); loadInitialData(); - return view; + return binding.getRoot(); } /** @@ -167,15 +137,15 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis private void setupRecyclerViews() { // Set up Drawer menu to select conversation chatAdapter = new ChatAdapter(chatList, this); - rvChatList.setLayoutManager(new LinearLayoutManager(getContext())); - rvChatList.setAdapter(chatAdapter); + binding.rvChatList.setLayoutManager(new LinearLayoutManager(getContext())); + binding.rvChatList.setAdapter(chatAdapter); // set up RecyclerView for selected chat to show messages messageAdapter = new MessageAdapter(messageList, null); LinearLayoutManager lm = new LinearLayoutManager(getContext()); lm.setStackFromEnd(true); - rvMessages.setLayoutManager(lm); - rvMessages.setAdapter(messageAdapter); + binding.rvMessages.setLayoutManager(lm); + binding.rvMessages.setAdapter(messageAdapter); setConversationActive(false); } @@ -247,7 +217,7 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis // Update title to customer name of active conversation for (Chat chat : chatList) { if (chat.getChatId().equals(String.valueOf(activeConversationId))) { - tvChatTitle.setText(chat.getCustomerName()); + binding.tvChatTitle.setText(chat.getCustomerName()); break; } } @@ -270,8 +240,8 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis public void onChatClick(Chat chat) { activeConversationId = Long.parseLong(chat.getChatId()); setConversationActive(true); - tvChatTitle.setText(chat.getCustomerName()); - drawerLayout.closeDrawer(GravityCompat.START); + binding.tvChatTitle.setText(chat.getCustomerName()); + binding.chatDrawerLayout.closeDrawer(GravityCompat.START); if (stompChatManager != null) { stompChatManager.subscribeToConversation(activeConversationId); @@ -302,11 +272,11 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis if (activeConversationId == null) return; //get the message from text field - String text = etMessage.getText().toString().trim(); + String text = binding.etMessage.getText().toString().trim(); if (text.isEmpty()) return; //clear text field after sending - etMessage.setText(""); + binding.etMessage.setText(""); //calls api to send the message messageApi.sendMessage(activeConversationId, new SendMessageRequest(text)) @@ -332,18 +302,18 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis */ private void showAttachmentPreview(Uri uri) { pendingAttachmentUri = uri; - layoutAttachmentPreview.setVisibility(View.VISIBLE); + binding.layoutAttachmentPreview.setVisibility(View.VISIBLE); String mimeType = requireContext().getContentResolver().getType(uri); String fileName = getFileName(uri); - tvPreviewName.setText(fileName); + binding.tvPreviewName.setText(fileName); // If the file is an image, display a thumbnail of the image as well if (mimeType != null && mimeType.startsWith("image/")) { - ivPreview.setVisibility(View.VISIBLE); - Glide.with(this).load(uri).into(ivPreview); + binding.ivPreview.setVisibility(View.VISIBLE); + Glide.with(this).load(uri).into(binding.ivPreview); } else { - ivPreview.setVisibility(View.GONE); + binding.ivPreview.setVisibility(View.GONE); } } @@ -352,7 +322,7 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis */ private void removeAttachment() { pendingAttachmentUri = null; - layoutAttachmentPreview.setVisibility(View.GONE); + binding.layoutAttachmentPreview.setVisibility(View.GONE); } /** @@ -448,7 +418,7 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis if (activeConversationId != null && activeConversationId.equals(dto.getId())) { setConversationActive(true); - tvChatTitle.setText(name); + binding.tvChatTitle.setText(name); } } @@ -513,8 +483,8 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis */ private void scrollToBottom() { if (!messageList.isEmpty()) { - rvMessages.post(() -> - rvMessages.smoothScrollToPosition(messageList.size() - 1)); + binding.rvMessages.post(() -> + binding.rvMessages.smoothScrollToPosition(messageList.size() - 1)); } } @@ -546,23 +516,23 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis * Toggles the UI state based on whether a conversation is currently selected. */ private void setConversationActive(boolean active) { - btnSend.setEnabled(active); - etMessage.setEnabled(active); - btnAttach.setEnabled(active); + binding.btnSend.setEnabled(active); + binding.etMessage.setEnabled(active); + binding.btnAttach.setEnabled(active); if (!active) { activeConversationId = null; ChatNotificationService.activeConversationIdInUi = null; removeAttachment(); - if (tvChatTitle != null) tvChatTitle.setText("Customer Chat"); + if (binding != null && binding.tvChatTitle != null) binding.tvChatTitle.setText("Customer Chat"); if (stompChatManager != null) { stompChatManager.clearConversationSubscription(); } messageList.clear(); messageAdapter.notifyDataSetChanged(); - etMessage.setText(""); - etMessage.setHint("Select a chat to start messaging"); + binding.etMessage.setText(""); + binding.etMessage.setHint("Select a chat to start messaging"); } else { - etMessage.setHint("Type a message..."); + binding.etMessage.setHint("Type a message..."); ChatNotificationService.activeConversationIdInUi = activeConversationId; } } @@ -573,6 +543,7 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis @Override public void onDestroyView() { super.onDestroyView(); + binding = null; ChatNotificationService.activeConversationIdInUi = null; if (stompChatManager != null) stompChatManager.disconnect(); } diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/ListFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/ListFragment.java index 5a2dcfc6..f2621091 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/ListFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/ListFragment.java @@ -14,11 +14,10 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.LinearLayout; import com.example.petstoremobile.R; - import com.example.petstoremobile.api.auth.TokenManager; +import com.example.petstoremobile.databinding.FragmentListBinding; import javax.inject.Inject; @@ -28,12 +27,7 @@ import dagger.hilt.android.AndroidEntryPoint; @AndroidEntryPoint public class ListFragment extends Fragment { - private DrawerLayout drawerLayout; - private LinearLayout drawerPets, drawerServices, drawerSuppliers; - private View touchBlocker; - - private LinearLayout drawerAdoptions, drawerAppointments, drawerInventory, drawerProducts, drawerProductSupplier, drawerPurchaseOrderView, drawerSale; - + private FragmentListBinding binding; private NavController innerNavController; @Inject TokenManager tokenManager; @@ -42,49 +36,33 @@ public class ListFragment extends Fragment { * Inflates the fragment layout, initializes navigation drawers, and applies role-based access control. */ @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_list, container, false); - - //get controls from the layout - drawerLayout = view.findViewById(R.id.drawerLayout); - drawerPets = view.findViewById(R.id.drawerPets); - drawerServices = view.findViewById(R.id.drawerServices); - drawerSuppliers = view.findViewById(R.id.drawerSuppliers); - drawerAdoptions = view.findViewById(R.id.drawerAdoptions); - drawerAppointments = view.findViewById(R.id.drawerAppointments); - drawerInventory = view.findViewById(R.id.drawerInventory); - drawerProducts = view.findViewById(R.id.drawerProducts); - drawerProductSupplier=view.findViewById(R.id.drawerProductSupplier); - drawerSale=view.findViewById(R.id.drawerSale); - drawerPurchaseOrderView=view.findViewById(R.id.drawerPurchaseOrderView); + binding = FragmentListBinding.inflate(inflater, container, false); // Check user role and restrict access for STAFF String role = tokenManager.getRole(); if ("STAFF".equalsIgnoreCase(role)) { - drawerSuppliers.setVisibility(View.GONE); - drawerInventory.setVisibility(View.GONE); + binding.drawerSuppliers.setVisibility(View.GONE); + binding.drawerInventory.setVisibility(View.GONE); } - //needed to disable touches on the innerContainer while the drawer is open - touchBlocker = view.findViewById(R.id.touchBlocker); - //add Listeners to the drawer so user won't be able to interact with the innerContainer (the list fragments) //while the drawer is open - drawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() { + binding.drawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() { //When the drawer is opened, disable touches on the background @Override public void onDrawerOpened(View drawerView) { - touchBlocker.setVisibility(View.VISIBLE); - touchBlocker.setClickable(true); + binding.touchBlocker.setVisibility(View.VISIBLE); + binding.touchBlocker.setClickable(true); } //When the drawer is closed, enable touches again @Override public void onDrawerClosed(View drawerView) { - touchBlocker.setVisibility(View.GONE); - touchBlocker.setClickable(false); + binding.touchBlocker.setVisibility(View.GONE); + binding.touchBlocker.setClickable(false); } //unused methods @@ -95,18 +73,24 @@ public class ListFragment extends Fragment { }); // Click listeners for each drawer - drawerPets.setOnClickListener(v -> navigateTo(R.id.nav_pet)); - drawerServices.setOnClickListener(v -> navigateTo(R.id.nav_service)); - drawerSuppliers.setOnClickListener(v -> navigateTo(R.id.nav_supplier)); - drawerAdoptions.setOnClickListener(v -> navigateTo(R.id.nav_adoption)); - drawerAppointments.setOnClickListener(v -> navigateTo(R.id.nav_appointment)); - drawerInventory.setOnClickListener(v -> navigateTo(R.id.nav_inventory)); - drawerProducts.setOnClickListener(v -> navigateTo(R.id.nav_product)); - drawerProductSupplier.setOnClickListener(v -> navigateTo(R.id.nav_product_supplier)); - drawerPurchaseOrderView.setOnClickListener(v -> navigateTo(R.id.nav_purchase_order)); - drawerSale.setOnClickListener(v -> navigateTo(R.id.nav_sale)); + binding.drawerPets.setOnClickListener(v -> navigateTo(R.id.nav_pet)); + binding.drawerServices.setOnClickListener(v -> navigateTo(R.id.nav_service)); + binding.drawerSuppliers.setOnClickListener(v -> navigateTo(R.id.nav_supplier)); + binding.drawerAdoptions.setOnClickListener(v -> navigateTo(R.id.nav_adoption)); + binding.drawerAppointments.setOnClickListener(v -> navigateTo(R.id.nav_appointment)); + binding.drawerInventory.setOnClickListener(v -> navigateTo(R.id.nav_inventory)); + binding.drawerProducts.setOnClickListener(v -> navigateTo(R.id.nav_product)); + binding.drawerProductSupplier.setOnClickListener(v -> navigateTo(R.id.nav_product_supplier)); + binding.drawerPurchaseOrderView.setOnClickListener(v -> navigateTo(R.id.nav_purchase_order)); + binding.drawerSale.setOnClickListener(v -> navigateTo(R.id.nav_sale)); - return view; + return binding.getRoot(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; } /** @@ -129,13 +113,13 @@ public class ListFragment extends Fragment { if (innerNavController != null) { innerNavController.navigate(destinationId); } - drawerLayout.closeDrawers(); + binding.drawerLayout.closeDrawers(); } /** * Programmatically opens the navigation drawer. */ public void openDrawer() { - drawerLayout.openDrawer(GravityCompat.START); + binding.drawerLayout.openDrawer(GravityCompat.START); } } diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/ProfileFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/ProfileFragment.java index bcc8a0d8..9d8a472f 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/ProfileFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/ProfileFragment.java @@ -3,6 +3,7 @@ package com.example.petstoremobile.fragments; import android.net.Uri; import android.os.Bundle; +import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; @@ -10,19 +11,16 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.Button; import android.widget.EditText; -import android.widget.ImageView; -import android.widget.TextView; import android.widget.Toast; import com.example.petstoremobile.R; import com.example.petstoremobile.activities.MainActivity; import com.example.petstoremobile.api.auth.AuthApi; import com.example.petstoremobile.api.auth.TokenManager; +import com.example.petstoremobile.databinding.FragmentProfileBinding; import com.example.petstoremobile.dtos.UserDTO; import com.example.petstoremobile.services.ChatNotificationService; -import com.example.petstoremobile.utils.ErrorUtils; import com.example.petstoremobile.utils.FileUtils; import com.example.petstoremobile.utils.GlideUtils; import com.example.petstoremobile.utils.ImagePickerHelper; @@ -41,9 +39,6 @@ import dagger.hilt.android.AndroidEntryPoint; import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.RequestBody; -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; /** * Fragment that displays and allows editing of the user's profile information. @@ -51,9 +46,7 @@ import retrofit2.Response; @AndroidEntryPoint public class ProfileFragment extends Fragment { - //initialize the view/controls - private ImageView imgProfile; - private TextView tvProfileName, tvProfileEmail, tvProfilePhone, tvProfileRole; + private FragmentProfileBinding binding; private UserDTO currentUser; private boolean hasImage = false; @@ -87,37 +80,26 @@ public class ProfileFragment extends Fragment { * Inflates the fragment layout and sets up listeners for profile. */ @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_profile, container, false); - - //get all the controls from the view - imgProfile = view.findViewById(R.id.imgProfile); - tvProfileName = view.findViewById(R.id.tvProfileName); - tvProfileEmail = view.findViewById(R.id.tvProfileEmail); - tvProfilePhone = view.findViewById(R.id.tvProfilePhone); - tvProfileRole = view.findViewById(R.id.tvProfileRole); - Button btnChangePhoto = view.findViewById(R.id.btnChangePhoto); - Button btnEditEmail = view.findViewById(R.id.btnEditEmail); - Button btnEditPhone = view.findViewById(R.id.btnEditPhone); - Button btnLogout = view.findViewById(R.id.btnLogout); + binding = FragmentProfileBinding.inflate(inflater, container, false); //Load Profile Data from backend loadProfileData(); //Set up listeners for the buttons //Change photo button - btnChangePhoto.setOnClickListener(v -> { + binding.btnChangePhoto.setOnClickListener(v -> { imagePickerHelper.showImagePickerDialog("Change Profile Photo", hasImage); }); //Edit email button //When clicked open a dialog to change email - btnEditEmail.setOnClickListener(v -> { + binding.btnEditEmail.setOnClickListener(v -> { //Make a text field for the user to enter the new email EditText input = new EditText(requireContext()); input.setPadding(30,30,30,30); - input.setText(tvProfileEmail.getText().toString()); + input.setText(binding.tvProfileEmail.getText().toString()); //set input type to email input.setInputType(android.text.InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS); @@ -140,11 +122,11 @@ public class ProfileFragment extends Fragment { //Edit phone button //When clicked open a dialog to change phone - btnEditPhone.setOnClickListener(v -> { + binding.btnEditPhone.setOnClickListener(v -> { //Make a text field for the user to enter the new email EditText input = new EditText(requireContext()); input.setPadding(30,30,30,30); - input.setText(tvProfilePhone.getText().toString()); + input.setText(binding.tvProfilePhone.getText().toString()); //set input type to phone number input.setInputType(android.view.inputmethod.EditorInfo.TYPE_CLASS_PHONE); @@ -169,7 +151,7 @@ public class ProfileFragment extends Fragment { }); //Logout button - btnLogout.setOnClickListener(v -> { + binding.btnLogout.setOnClickListener(v -> { // Stop notification service before logging out so notifications stop android.content.Intent serviceIntent = new android.content.Intent(requireContext(), ChatNotificationService.class); requireContext().stopService(serviceIntent); @@ -183,7 +165,13 @@ public class ProfileFragment extends Fragment { requireActivity().finish(); }); - return view; + return binding.getRoot(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; } /** @@ -194,16 +182,16 @@ public class ProfileFragment extends Fragment { currentUser = result; //set the user data to the view - tvProfileName.setText(currentUser.getFullName()); - tvProfileEmail.setText(currentUser.getEmail()); - tvProfilePhone.setText(currentUser.getPhone()); - tvProfileRole.setText(currentUser.getRole()); + binding.tvProfileName.setText(currentUser.getFullName()); + binding.tvProfileEmail.setText(currentUser.getEmail()); + binding.tvProfilePhone.setText(currentUser.getPhone()); + binding.tvProfileRole.setText(currentUser.getRole()); // get the avatar endpoint to load profile image and the token for authorization String avatarUrl = baseUrl + AuthApi.AVATAR_FILE_PATH; String token = tokenManager.getToken(); - GlideUtils.loadImageWithToken(requireContext(), imgProfile, avatarUrl, token, R.drawable.placeholder, new GlideUtils.ImageLoadListener() { + GlideUtils.loadImageWithToken(requireContext(), binding.imgProfile, avatarUrl, token, R.drawable.placeholder, new GlideUtils.ImageLoadListener() { @Override public void onResourceReady() { hasImage = true; @@ -246,7 +234,7 @@ public class ProfileFragment extends Fragment { private void deleteAvatar() { authApi.deleteAvatar().enqueue(RetrofitUtils.createCallback(requireContext(), "DELETE_AVATAR", "Avatar removed successfully", result -> { hasImage = false; - imgProfile.setImageResource(R.drawable.placeholder); + binding.imgProfile.setImageResource(R.drawable.placeholder); })); } @@ -260,8 +248,8 @@ public class ProfileFragment extends Fragment { authApi.updateMe(updates).enqueue(RetrofitUtils.createCallback(requireContext(), "UPDATE_PROFILE", "Profile updated successfully", result -> { currentUser = result; // Update the view with the new data from backend - tvProfileEmail.setText(currentUser.getEmail()); - tvProfilePhone.setText(currentUser.getPhone()); + binding.tvProfileEmail.setText(currentUser.getEmail()); + binding.tvProfilePhone.setText(currentUser.getPhone()); })); } } diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AdoptionFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AdoptionFragment.java index cae5fd2f..04713d86 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AdoptionFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AdoptionFragment.java @@ -2,7 +2,6 @@ package com.example.petstoremobile.fragments.listfragments; import android.graphics.Color; import android.os.Bundle; -import android.text.*; import android.util.Log; import android.view.*; import android.widget.*; @@ -12,20 +11,17 @@ 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.databinding.FragmentAdoptionBinding; import com.example.petstoremobile.dtos.AdoptionDTO; 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; import com.prolificinteractive.materialcalendarview.CalendarMode; import com.prolificinteractive.materialcalendarview.MaterialCalendarView; -import com.prolificinteractive.materialcalendarview.OnDateSelectedListener; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; @@ -35,15 +31,11 @@ import dagger.hilt.android.AndroidEntryPoint; @AndroidEntryPoint public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdoptionClickListener { + private FragmentAdoptionBinding binding; private List adoptionList = new ArrayList<>(); private List filteredList = new ArrayList<>(); private AdoptionAdapter adapter; private AdoptionViewModel viewModel; - private SwipeRefreshLayout swipeRefresh; - private EditText etSearch; - private ImageButton hamburger; - private ImageButton btnToggleCalendarMode; - private MaterialCalendarView calendarView; private CalendarDay selectedCalendarDay; private boolean isMonthMode = false; private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); @@ -63,22 +55,17 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_adoption, container, false); + binding = FragmentAdoptionBinding.inflate(inflater, container, false); - hamburger = view.findViewById(R.id.btnHamburgerAdoption); - calendarView = view.findViewById(R.id.calendarViewAdoption); - btnToggleCalendarMode = view.findViewById(R.id.btnToggleCalendarModeAdoption); - - setupRecyclerView(view); - setupSearch(view); - setupSwipeRefresh(view); + setupRecyclerView(); + setupSearch(); + setupSwipeRefresh(); setupCalendar(); loadAdoptions(); - FloatingActionButton fab = view.findViewById(R.id.fabAddAdoption); - fab.setOnClickListener(v -> openDetail(-1)); + binding.fabAddAdoption.setOnClickListener(v -> openDetail(-1)); - hamburger.setOnClickListener(v -> { + binding.btnHamburgerAdoption.setOnClickListener(v -> { Fragment parent = getParentFragment(); if (parent != null) { Fragment grandParent = parent.getParentFragment(); @@ -88,9 +75,15 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop } }); - btnToggleCalendarMode.setOnClickListener(v -> toggleCalendarMode()); + binding.btnToggleCalendarModeAdoption.setOnClickListener(v -> toggleCalendarMode()); - return view; + return binding.getRoot(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; } /** @@ -98,7 +91,7 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop */ private void toggleCalendarMode() { isMonthMode = !isMonthMode; - calendarView.state().edit() + binding.calendarViewAdoption.state().edit() .setCalendarDisplayMode(isMonthMode ? CalendarMode.MONTHS : CalendarMode.WEEKS) .commit(); } @@ -107,21 +100,18 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop * Sets up the date selection listener for the calendar. */ private void setupCalendar() { - calendarView.setOnDateChangedListener(new OnDateSelectedListener() { - @Override - public void onDateSelected(@NonNull MaterialCalendarView widget, @NonNull CalendarDay date, boolean selected) { - if (selected) { - if (date.equals(selectedCalendarDay)) { - selectedCalendarDay = null; - calendarView.clearSelection(); - } else { - selectedCalendarDay = date; - } - } else { + binding.calendarViewAdoption.setOnDateChangedListener((widget, date, selected) -> { + if (selected) { + if (date.equals(selectedCalendarDay)) { selectedCalendarDay = null; + binding.calendarViewAdoption.clearSelection(); + } else { + selectedCalendarDay = date; } - filter(etSearch.getText().toString()); + } else { + selectedCalendarDay = null; } + filter(binding.etSearchAdoption.getText().toString()); }); } @@ -144,28 +134,26 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop Log.e("AdoptionFragment", "Error parsing date: " + adoption.getAdoptionDate()); } } - calendarView.removeDecorators(); - calendarView.addDecorator(new EventDecorator(Color.RED, datesWithAdoptions)); + binding.calendarViewAdoption.removeDecorators(); + binding.calendarViewAdoption.addDecorator(new EventDecorator(Color.RED, datesWithAdoptions)); } /** * Initializes the RecyclerView for displaying adoptions. */ - private void setupRecyclerView(View view) { - RecyclerView rv = view.findViewById(R.id.recyclerViewAdoptions); + private void setupRecyclerView() { adapter = new AdoptionAdapter(filteredList, this); - rv.setLayoutManager(new LinearLayoutManager(getContext())); - rv.setAdapter(adapter); + binding.recyclerViewAdoptions.setLayoutManager(new LinearLayoutManager(getContext())); + binding.recyclerViewAdoptions.setAdapter(adapter); } /** * Sets up the search bar for filtering */ - private void setupSearch(View view) { - etSearch = view.findViewById(R.id.etSearchAdoption); - etSearch.addTextChangedListener(new TextWatcher() { + private void setupSearch() { + binding.etSearchAdoption.addTextChangedListener(new android.text.TextWatcher() { public void beforeTextChanged(CharSequence s, int a, int b, int c) {} - public void afterTextChanged(Editable s) {} + public void afterTextChanged(android.text.Editable s) {} public void onTextChanged(CharSequence s, int a, int b, int c) { filter(s.toString()); } @@ -175,9 +163,8 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop /** * Sets up the SwipeRefreshLayout to reload adoption data. */ - private void setupSwipeRefresh(View view) { - swipeRefresh = view.findViewById(R.id.swipeRefreshAdoption); - swipeRefresh.setOnRefreshListener(this::loadAdoptions); + private void setupSwipeRefresh() { + binding.swipeRefreshAdoption.setOnRefreshListener(this::loadAdoptions); } /** @@ -221,21 +208,21 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop switch (resource.status) { case LOADING: // Show loading indicator - if (swipeRefresh != null) swipeRefresh.setRefreshing(true); + binding.swipeRefreshAdoption.setRefreshing(true); break; case SUCCESS: // Hide loading indicator and display data - if (swipeRefresh != null) swipeRefresh.setRefreshing(false); + binding.swipeRefreshAdoption.setRefreshing(false); if (resource.data != null) { adoptionList.clear(); adoptionList.addAll(resource.data.getContent()); updateCalendarDecorators(); - filter(etSearch != null ? etSearch.getText().toString() : ""); + filter(binding.etSearchAdoption != null ? binding.etSearchAdoption.getText().toString() : ""); } break; case ERROR: // Hide loading indicator and toast error message - if (swipeRefresh != null) swipeRefresh.setRefreshing(false); + binding.swipeRefreshAdoption.setRefreshing(false); Toast.makeText(getContext(), "Failed to load adoptions: " + resource.message, Toast.LENGTH_SHORT).show(); Log.e("AdoptionFragment", "Error loading adoptions: " + resource.message); break; diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AppointmentFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AppointmentFragment.java index f62475c0..fb1455ae 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AppointmentFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AppointmentFragment.java @@ -9,20 +9,18 @@ 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 android.text.Editable; import android.text.TextWatcher; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.EditText; -import android.widget.ImageButton; import android.widget.Toast; import com.example.petstoremobile.R; import com.example.petstoremobile.adapters.AppointmentAdapter; +import com.example.petstoremobile.databinding.FragmentAppointmentBinding; import com.example.petstoremobile.dtos.AppointmentDTO; import com.example.petstoremobile.dtos.ServiceDTO; import com.example.petstoremobile.dtos.PetDTO; @@ -32,11 +30,8 @@ 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; import com.prolificinteractive.materialcalendarview.CalendarMode; -import com.prolificinteractive.materialcalendarview.MaterialCalendarView; -import com.prolificinteractive.materialcalendarview.OnDateSelectedListener; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -52,6 +47,7 @@ import dagger.hilt.android.AndroidEntryPoint; @AndroidEntryPoint public class AppointmentFragment extends Fragment implements AppointmentAdapter.OnAppointmentClickListener { + private FragmentAppointmentBinding binding; private List appointmentList = new ArrayList<>(); private List filteredList = new ArrayList<>(); private List petList = new ArrayList<>(); @@ -62,11 +58,6 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter. private PetViewModel petViewModel; private ServiceViewModel serviceViewModel; - private SwipeRefreshLayout swipeRefreshLayout; - private EditText etSearch; - private ImageButton hamburger; - private ImageButton btnToggleCalendarMode; - private MaterialCalendarView calendarView; private CalendarDay selectedCalendarDay; private boolean isMonthMode = false; private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); @@ -88,24 +79,19 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter. @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_appointment, container, false); + binding = FragmentAppointmentBinding.inflate(inflater, container, false); - hamburger = view.findViewById(R.id.btnHamburger); - calendarView = view.findViewById(R.id.calendarView); - btnToggleCalendarMode = view.findViewById(R.id.btnToggleCalendarMode); - - setupRecyclerView(view); - setupSearch(view); - setupSwipeRefresh(view); + setupRecyclerView(); + setupSearch(); + setupSwipeRefresh(); setupCalendar(); loadAppointmentData(); loadPets(); loadServices(); - FloatingActionButton fabAdd = view.findViewById(R.id.fabAddAppointment); - fabAdd.setOnClickListener(v -> openAppointmentDetails(-1)); + binding.fabAddAppointment.setOnClickListener(v -> openAppointmentDetails(-1)); - hamburger.setOnClickListener(v -> { + binding.btnHamburger.setOnClickListener(v -> { Fragment parent = getParentFragment(); if (parent != null) { Fragment grandParent = parent.getParentFragment(); @@ -115,9 +101,15 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter. } }); - btnToggleCalendarMode.setOnClickListener(v -> toggleCalendarMode()); + binding.btnToggleCalendarMode.setOnClickListener(v -> toggleCalendarMode()); - return view; + return binding.getRoot(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; } /** @@ -125,7 +117,7 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter. */ private void toggleCalendarMode() { isMonthMode = !isMonthMode; - calendarView.state().edit() + binding.calendarView.state().edit() .setCalendarDisplayMode(isMonthMode ? CalendarMode.MONTHS : CalendarMode.WEEKS) .commit(); } @@ -134,21 +126,18 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter. * Sets up the date selection listener for the calendar. */ private void setupCalendar() { - calendarView.setOnDateChangedListener(new OnDateSelectedListener() { - @Override - public void onDateSelected(@NonNull MaterialCalendarView widget, @NonNull CalendarDay date, boolean selected) { - if (selected) { - if (date.equals(selectedCalendarDay)) { - selectedCalendarDay = null; - calendarView.clearSelection(); - } else { - selectedCalendarDay = date; - } - } else { + binding.calendarView.setOnDateChangedListener((widget, date, selected) -> { + if (selected) { + if (date.equals(selectedCalendarDay)) { selectedCalendarDay = null; + binding.calendarView.clearSelection(); + } else { + selectedCalendarDay = date; } - filterAppointments(etSearch.getText().toString()); + } else { + selectedCalendarDay = null; } + filterAppointments(binding.etSearchAppointment.getText().toString()); }); } @@ -172,16 +161,15 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter. } } //update the indicators to the calendar - calendarView.removeDecorators(); - calendarView.addDecorator(new EventDecorator(Color.RED, datesWithAppointments)); + binding.calendarView.removeDecorators(); + binding.calendarView.addDecorator(new EventDecorator(Color.RED, datesWithAppointments)); } /** * Configures the search bar for filtering. */ - private void setupSearch(View view) { - etSearch = view.findViewById(R.id.etSearchAppointment); - etSearch.addTextChangedListener(new TextWatcher() { + private void setupSearch() { + binding.etSearchAppointment.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @@ -229,9 +217,8 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter. /** * Initializes the SwipeRefreshLayout to allow manual data refreshing. */ - private void setupSwipeRefresh(View view) { - swipeRefreshLayout = view.findViewById(R.id.swipeRefreshAppointment); - swipeRefreshLayout.setOnRefreshListener(this::loadAppointmentData); + private void setupSwipeRefresh() { + binding.swipeRefreshAppointment.setOnRefreshListener(this::loadAppointmentData); } /** @@ -290,21 +277,21 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter. switch (resource.status) { case LOADING: // Show loading indicator - if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(true); + binding.swipeRefreshAppointment.setRefreshing(true); break; case SUCCESS: // Hide loading indicator and display data - if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false); + binding.swipeRefreshAppointment.setRefreshing(false); if (resource.data != null) { appointmentList.clear(); appointmentList.addAll(resource.data.getContent()); updateCalendarDecorators(); - filterAppointments(etSearch != null ? etSearch.getText().toString() : ""); + filterAppointments(binding.etSearchAppointment != null ? binding.etSearchAppointment.getText().toString() : ""); } break; case ERROR: // Hide loading indicator and toast error message - if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false); + binding.swipeRefreshAppointment.setRefreshing(false); Toast.makeText(getContext(), "Failed to load appointments: " + resource.message, Toast.LENGTH_SHORT).show(); Log.e("AppointmentFragment", "Error loading appointments: " + resource.message); break; @@ -336,34 +323,12 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter. }); } - /** - * Get a pet's name based on its ID. - */ - private String getPetName(Long id) { - for (PetDTO p : petList) { - if (p.getPetId().equals(id)) return p.getPetName(); - - } - return ""; - } - - /** - * Get a service's name based on its ID. - */ - private String getServiceName(Long id) { - for (ServiceDTO s : serviceList) { - if (s.getServiceId().equals(id))return s.getServiceName(); - } - return ""; - } - /** * Initializes the RecyclerView for displaying appointments. */ - private void setupRecyclerView(View view) { - RecyclerView recyclerView = view.findViewById(R.id.recyclerViewAppointments); + private void setupRecyclerView() { adapter = new AppointmentAdapter(filteredList, this); - recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); - recyclerView.setAdapter(adapter); + binding.recyclerViewAppointments.setLayoutManager(new LinearLayoutManager(getContext())); + binding.recyclerViewAppointments.setAdapter(adapter); } } diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/InventoryFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/InventoryFragment.java index 799fe39f..07e08aba 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/InventoryFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/InventoryFragment.java @@ -10,11 +10,6 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; -import android.widget.Button; -import android.widget.EditText; -import android.widget.ImageButton; -import android.widget.Spinner; -import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; @@ -24,11 +19,11 @@ 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.BlackTextArrayAdapter; import com.example.petstoremobile.adapters.InventoryAdapter; +import com.example.petstoremobile.databinding.FragmentInventoryBinding; import com.example.petstoremobile.dtos.CategoryDTO; import com.example.petstoremobile.dtos.InventoryDTO; import com.example.petstoremobile.fragments.ListFragment; @@ -46,18 +41,12 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn private static final String TAG = "InventoryFragment"; private static final int PAGE_SIZE = 20; + private FragmentInventoryBinding binding; private final List inventoryList = new ArrayList<>(); private final List categoryList = new ArrayList<>(); private InventoryAdapter adapter; private InventoryViewModel viewModel; - private SwipeRefreshLayout swipeRefreshLayout; - private EditText etSearch; - private Spinner spinnerCategory; - private ImageButton hamburger; - private Button btnBulkDelete; - private TextView tvSelectionCount; - // Debounce search private final Handler searchHandler = new Handler(Looper.getMainLooper()); private Runnable searchRunnable; @@ -89,23 +78,17 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_inventory, container, false); + binding = FragmentInventoryBinding.inflate(inflater, container, false); - hamburger = view.findViewById(R.id.btnHamburger); - btnBulkDelete = view.findViewById(R.id.btnBulkDelete); - tvSelectionCount = view.findViewById(R.id.tvSelectionCount); - spinnerCategory = view.findViewById(R.id.spinnerCategory); - - setupRecyclerView(view); - setupSearch(view); - setupSwipeRefresh(view); + setupRecyclerView(); + setupSearch(); + setupSwipeRefresh(); loadCategories(); // loads categories then triggers loadInventory loadInventory(true); - view.findViewById(R.id.fabAddInventory) - .setOnClickListener(v -> openDetail(null)); + binding.fabAddInventory.setOnClickListener(v -> openDetail(null)); - hamburger.setOnClickListener(v -> { + binding.btnHamburger.setOnClickListener(v -> { Fragment parent = getParentFragment(); if (parent != null) { Fragment grandParent = parent.getParentFragment(); @@ -115,9 +98,15 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn } }); - btnBulkDelete.setOnClickListener(v -> confirmBulkDelete()); + binding.btnBulkDelete.setOnClickListener(v -> confirmBulkDelete()); - return view; + return binding.getRoot(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; } /** @@ -153,9 +142,9 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn android.R.layout.simple_spinner_item, categoryNames); spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - spinnerCategory.setAdapter(spinnerAdapter); + binding.spinnerCategory.setAdapter(spinnerAdapter); - spinnerCategory.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + binding.spinnerCategory.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { if (!spinnerReady) { @@ -181,15 +170,12 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn /** * Sets up the search bar for filtering. */ - private void setupSearch(View view) { - etSearch = view.findViewById(R.id.etSearchInventory); - etSearch.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int i, int i1, int i2) { + private void setupSearch() { + binding.etSearchInventory.addTextChangedListener(new TextWatcher() { + @Override public void beforeTextChanged(CharSequence s, int i, int i1, int i2) { } - @Override - public void afterTextChanged(Editable s) { + @Override public void afterTextChanged(Editable s) { } @Override @@ -208,18 +194,17 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn /** * Initializes the RecyclerView with a layout manager, and adapter. */ - private void setupRecyclerView(View view) { - RecyclerView rv = view.findViewById(R.id.recyclerViewInventory); + private void setupRecyclerView() { adapter = new InventoryAdapter(inventoryList, this); - rv.setLayoutManager(new LinearLayoutManager(getContext())); - rv.setAdapter(adapter); + binding.recyclerViewInventory.setLayoutManager(new LinearLayoutManager(getContext())); + binding.recyclerViewInventory.setAdapter(adapter); - rv.addOnScrollListener(new RecyclerView.OnScrollListener() { + binding.recyclerViewInventory.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { if (dy <= 0) return; - LinearLayoutManager lm = (LinearLayoutManager) rv.getLayoutManager(); + LinearLayoutManager lm = (LinearLayoutManager) binding.recyclerViewInventory.getLayoutManager(); if (lm == null) return; int visible = lm.getChildCount(); @@ -235,9 +220,8 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn /** * Sets up the SwipeRefreshLayout to reload the first page of inventory items. */ - private void setupSwipeRefresh(View view) { - swipeRefreshLayout = view.findViewById(R.id.swipeRefreshInventory); - swipeRefreshLayout.setOnRefreshListener(() -> loadInventory(true)); + private void setupSwipeRefresh() { + binding.swipeRefreshInventory.setOnRefreshListener(() -> loadInventory(true)); } /** @@ -264,12 +248,12 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn case LOADING: // Show loading indicator isLoading = true; - if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(true); + binding.swipeRefreshInventory.setRefreshing(true); break; case SUCCESS: // Hide loading indicator and display data isLoading = false; - if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false); + binding.swipeRefreshInventory.setRefreshing(false); if (resource.data != null) { if (reset) inventoryList.clear(); inventoryList.addAll(resource.data.getContent()); @@ -281,7 +265,7 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn case ERROR: // Hide loading indicator and toast error message isLoading = false; - if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false); + binding.swipeRefreshInventory.setRefreshing(false); Log.e(TAG, "Error: " + resource.message); Toast.makeText(getContext(), "Failed to load inventory: " + resource.message, Toast.LENGTH_SHORT).show(); break; @@ -343,10 +327,10 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn * Hides the bulk deletion UI bar. */ private void hideBulkDeleteBar() { - if (btnBulkDelete != null) - btnBulkDelete.setVisibility(View.GONE); - if (tvSelectionCount != null) - tvSelectionCount.setVisibility(View.GONE); + if (binding != null) { + binding.btnBulkDelete.setVisibility(View.GONE); + binding.tvSelectionCount.setVisibility(View.GONE); + } } /** @@ -389,9 +373,9 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn @Override public void onSelectionChanged(int selectedCount) { if (selectedCount > 0) { - btnBulkDelete.setVisibility(View.VISIBLE); - tvSelectionCount.setVisibility(View.VISIBLE); - tvSelectionCount.setText(selectedCount + " selected"); + binding.btnBulkDelete.setVisibility(View.VISIBLE); + binding.tvSelectionCount.setVisibility(View.VISIBLE); + binding.tvSelectionCount.setText(selectedCount + " selected"); } else { hideBulkDeleteBar(); } diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/PetFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/PetFragment.java index 44198344..0c71593a 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/PetFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/PetFragment.java @@ -2,13 +2,12 @@ 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.lifecycle.ViewModelProvider; import androidx.navigation.fragment.NavHostFragment; import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import android.text.Editable; import android.text.TextWatcher; @@ -17,18 +16,15 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; -import android.widget.EditText; -import android.widget.ImageButton; -import android.widget.Spinner; 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.databinding.FragmentPetBinding; import com.example.petstoremobile.dtos.PetDTO; import com.example.petstoremobile.fragments.ListFragment; import com.example.petstoremobile.viewmodels.PetViewModel; -import com.google.android.material.floatingactionbutton.FloatingActionButton; import java.util.ArrayList; import java.util.List; @@ -40,16 +36,13 @@ import dagger.hilt.android.AndroidEntryPoint; @AndroidEntryPoint public class PetFragment extends Fragment implements PetAdapter.OnPetClickListener { + private FragmentPetBinding binding; private List petList = new ArrayList<>(); private List filteredList = new ArrayList<>(); - private ImageButton hamburger; private PetAdapter adapter; private PetViewModel viewModel; @Inject @Named("baseUrl") String baseUrl; - private SwipeRefreshLayout swipeRefreshLayout; - private EditText etSearch; - private Spinner spinnerStatus; /** * Initializes the fragment and its associated PetViewModel. @@ -64,21 +57,18 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen * Sets up the fragment's UI components, including RecyclerView, search, status filter, and swipe-to-refresh. */ @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_pet, container, false); + binding = FragmentPetBinding.inflate(inflater, container, false); - hamburger = view.findViewById(R.id.btnHamburger); + setupRecyclerView(); + setupSearch(); + setupStatusFilter(); + setupSwipeRefresh(); - setupRecyclerView(view); - setupSearch(view); - setupStatusFilter(view); - setupSwipeRefresh(view); + binding.fabAddPet.setOnClickListener(v -> openPetDetails()); - FloatingActionButton fabAddPet = view.findViewById(R.id.fabAddPet); - fabAddPet.setOnClickListener(v -> openPetDetails()); - - hamburger.setOnClickListener(v -> { + binding.btnHamburger.setOnClickListener(v -> { Fragment parent = getParentFragment(); if (parent != null) { Fragment grandParent = parent.getParentFragment(); @@ -88,7 +78,13 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen } }); - return view; + return binding.getRoot(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; } /** @@ -103,9 +99,8 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen /** * Configures the search bar with a for filtering. */ - private void setupSearch(View view) { - etSearch = view.findViewById(R.id.etSearchPet); - etSearch.addTextChangedListener(new TextWatcher() { + private void setupSearch() { + binding.etSearchPet.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void onTextChanged(CharSequence s, int start, int before, int count) { filterPets(); @@ -117,14 +112,13 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen /** * Configures the status filter spinner. */ - private void setupStatusFilter(View view) { - spinnerStatus = view.findViewById(R.id.spinnerStatus); + private void setupStatusFilter() { String[] statuses = {"All Statuses", "Available", "Adopted"}; BlackTextArrayAdapter adapter = new BlackTextArrayAdapter<>(requireContext(), android.R.layout.simple_spinner_item, statuses); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - spinnerStatus.setAdapter(adapter); + binding.spinnerStatus.setAdapter(adapter); - spinnerStatus.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + binding.spinnerStatus.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { filterPets(); @@ -139,8 +133,8 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen * Filters the pet list based on both the search query and the selected status. */ private void filterPets() { - String query = etSearch.getText().toString().toLowerCase(); - String selectedStatus = spinnerStatus.getSelectedItem().toString(); + String query = binding.etSearchPet.getText().toString().toLowerCase(); + String selectedStatus = binding.spinnerStatus.getSelectedItem().toString(); filteredList.clear(); for (PetDTO p : petList) { @@ -162,9 +156,8 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen /** * Sets up the SwipeRefreshLayout to allow manual re-fetching of pet data. */ - private void setupSwipeRefresh(View view) { - swipeRefreshLayout = view.findViewById(R.id.swipeRefreshPet); - swipeRefreshLayout.setOnRefreshListener(this::loadPetData); + private void setupSwipeRefresh() { + binding.swipeRefreshPet.setOnRefreshListener(this::loadPetData); } /** @@ -208,19 +201,15 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen * Fetches all pet data from the server via the ViewModel and updates the UI. */ private void loadPetData() { - //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); + binding.swipeRefreshPet.setRefreshing(true); break; case SUCCESS: - // Hide loading indicator and display data - if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false); + binding.swipeRefreshPet.setRefreshing(false); if (resource.data != null) { petList.clear(); petList.addAll(resource.data.getContent()); @@ -228,8 +217,7 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen } break; case ERROR: - // Hide loading indicator and toast error message - if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false); + binding.swipeRefreshPet.setRefreshing(false); Toast.makeText(getContext(), "Failed to load pets: " + resource.message, Toast.LENGTH_SHORT).show(); Log.e("PetFragment", "Error loading pets: " + resource.message); break; @@ -240,11 +228,10 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen /** * Initializes the RecyclerView with a layout manager and adapter for displaying pets. */ - private void setupRecyclerView(View view) { - RecyclerView recyclerView = view.findViewById(R.id.recyclerViewPets); + private void setupRecyclerView() { adapter = new PetAdapter(filteredList, this); adapter.setBaseUrl(baseUrl); - recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); - recyclerView.setAdapter(adapter); + binding.recyclerViewPets.setLayoutManager(new LinearLayoutManager(getContext())); + binding.recyclerViewPets.setAdapter(adapter); } } diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ProductFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ProductFragment.java index 61adfcf5..a459262f 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ProductFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ProductFragment.java @@ -11,16 +11,15 @@ 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.auth.TokenManager; +import com.example.petstoremobile.databinding.FragmentProductBinding; 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.*; import javax.inject.Inject; @@ -31,11 +30,10 @@ import dagger.hilt.android.AndroidEntryPoint; @AndroidEntryPoint public class ProductFragment extends Fragment implements ProductAdapter.OnProductClickListener { + private FragmentProductBinding binding; private List productList = new ArrayList<>(); private List filteredList = new ArrayList<>(); private ProductAdapter adapter; - private SwipeRefreshLayout swipeRefresh; - private EditText etSearch; private ProductViewModel viewModel; @Inject @Named("baseUrl") String baseUrl; @@ -56,19 +54,17 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_product, container, false); + binding = FragmentProductBinding.inflate(inflater, container, false); - setupRecyclerView(view); - setupSearch(view); - setupSwipeRefresh(view); + setupRecyclerView(); + setupSearch(); + setupSwipeRefresh(); loadProducts(); - FloatingActionButton fab = view.findViewById(R.id.fabAddProduct); - fab.setOnClickListener(v -> openDetail(-1)); + binding.fabAddProduct.setOnClickListener(v -> openDetail(-1)); - ImageButton hamburger = view.findViewById(R.id.btnHamburgerProduct); - hamburger.setOnClickListener(v -> { + binding.btnHamburgerProduct.setOnClickListener(v -> { Fragment parent = getParentFragment(); if (parent != null) { Fragment grandParent = parent.getParentFragment(); @@ -78,27 +74,31 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc } }); - return view; + return binding.getRoot(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; } /** * Initializes the RecyclerView with a layout manager and adapter for displaying products. */ - private void setupRecyclerView(View view) { - RecyclerView rv = view.findViewById(R.id.recyclerViewProducts); + private void setupRecyclerView() { adapter = new ProductAdapter(filteredList, this); adapter.setBaseUrl(baseUrl); adapter.setToken(tokenManager.getToken()); - rv.setLayoutManager(new LinearLayoutManager(getContext())); - rv.setAdapter(adapter); + binding.recyclerViewProducts.setLayoutManager(new LinearLayoutManager(getContext())); + binding.recyclerViewProducts.setAdapter(adapter); } /** * Configures the search bar for filtering. */ - private void setupSearch(View view) { - etSearch = view.findViewById(R.id.etSearchProduct); - etSearch.addTextChangedListener(new TextWatcher() { + private void setupSearch() { + binding.etSearchProduct.addTextChangedListener(new TextWatcher() { public void beforeTextChanged(CharSequence s, int a, int b, int c) {} public void afterTextChanged(Editable s) {} public void onTextChanged(CharSequence s, int a, int b, int c) { @@ -110,16 +110,15 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc /** * Sets up the SwipeRefreshLayout to allow manual re-fetching of product data. */ - private void setupSwipeRefresh(View view) { - swipeRefresh = view.findViewById(R.id.swipeRefreshProduct); - swipeRefresh.setOnRefreshListener(this::loadProducts); + private void setupSwipeRefresh() { + binding.swipeRefreshProduct.setOnRefreshListener(this::loadProducts); } /** * Filters the product list based on the search query across name, category, and description. */ private void filter() { - String query = etSearch.getText().toString().toLowerCase(); + String query = binding.etSearchProduct.getText().toString().toLowerCase(); filteredList.clear(); for (ProductDTO p : productList) { @@ -139,19 +138,15 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc * Fetches all product data from the server through the ViewModel and updates the UI. */ private void loadProducts() { - //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); + binding.swipeRefreshProduct.setRefreshing(true); break; case SUCCESS: - // Hide loading indicator and display data - if (swipeRefresh != null) swipeRefresh.setRefreshing(false); + binding.swipeRefreshProduct.setRefreshing(false); if (resource.data != null) { productList.clear(); productList.addAll(resource.data.getContent()); @@ -159,8 +154,7 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc } break; case ERROR: - // Hide loading indicator and toast error message - if (swipeRefresh != null) swipeRefresh.setRefreshing(false); + binding.swipeRefreshProduct.setRefreshing(false); Toast.makeText(getContext(), "Failed to load products: " + resource.message, Toast.LENGTH_SHORT).show(); Log.e("ProductFragment", "Error loading products: " + resource.message); break; diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ProductSupplierFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ProductSupplierFragment.java index d77331ca..49eccc94 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ProductSupplierFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ProductSupplierFragment.java @@ -11,15 +11,14 @@ 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.databinding.FragmentProductSupplierBinding; import com.example.petstoremobile.dtos.ProductSupplierDTO; import com.example.petstoremobile.fragments.ListFragment; import com.example.petstoremobile.viewmodels.ProductSupplierViewModel; -import com.example.petstoremobile.utils.Resource; -import com.google.android.material.floatingactionbutton.FloatingActionButton; + import java.util.*; import dagger.hilt.android.AndroidEntryPoint; @@ -28,11 +27,10 @@ import dagger.hilt.android.AndroidEntryPoint; public class ProductSupplierFragment extends Fragment implements ProductSupplierAdapter.OnProductSupplierClickListener { + private FragmentProductSupplierBinding binding; private List psList = new ArrayList<>(); private List filteredList = new ArrayList<>(); private ProductSupplierAdapter adapter; - private SwipeRefreshLayout swipeRefresh; - private EditText etSearch; private ProductSupplierViewModel viewModel; /** @@ -50,18 +48,16 @@ public class ProductSupplierFragment extends Fragment @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_product_supplier, container, false); + binding = FragmentProductSupplierBinding.inflate(inflater, container, false); - setupRecyclerView(view); - setupSearch(view); - setupSwipeRefresh(view); + setupRecyclerView(); + setupSearch(); + setupSwipeRefresh(); loadData(); - FloatingActionButton fab = view.findViewById(R.id.fabAddPS); - fab.setOnClickListener(v -> openDetail(-1)); + binding.fabAddPS.setOnClickListener(v -> openDetail(-1)); - ImageButton hamburger = view.findViewById(R.id.btnHamburgerPS); - hamburger.setOnClickListener(v -> { + binding.btnHamburgerPS.setOnClickListener(v -> { Fragment parent = getParentFragment(); if (parent != null) { Fragment grandParent = parent.getParentFragment(); @@ -71,25 +67,29 @@ public class ProductSupplierFragment extends Fragment } }); - return view; + return binding.getRoot(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; } /** * Initializes the RecyclerView with a layout manager and adapter for product-supplier data. */ - private void setupRecyclerView(View view) { - RecyclerView rv = view.findViewById(R.id.recyclerViewPS); + private void setupRecyclerView() { adapter = new ProductSupplierAdapter(filteredList, this); - rv.setLayoutManager(new LinearLayoutManager(getContext())); - rv.setAdapter(adapter); + binding.recyclerViewPS.setLayoutManager(new LinearLayoutManager(getContext())); + binding.recyclerViewPS.setAdapter(adapter); } /** * Configures the search bar for filtering. */ - private void setupSearch(View view) { - etSearch = view.findViewById(R.id.etSearchPS); - etSearch.addTextChangedListener(new TextWatcher() { + private void setupSearch() { + binding.etSearchPS.addTextChangedListener(new TextWatcher() { public void beforeTextChanged(CharSequence s, int a, int b, int c) {} public void afterTextChanged(Editable s) {} public void onTextChanged(CharSequence s, int a, int b, int c) { @@ -101,9 +101,8 @@ public class ProductSupplierFragment extends Fragment /** * Sets up the SwipeRefreshLayout to allow manual reloading of product-supplier data. */ - private void setupSwipeRefresh(View view) { - swipeRefresh = view.findViewById(R.id.swipeRefreshPS); - swipeRefresh.setOnRefreshListener(this::loadData); + private void setupSwipeRefresh() { + binding.swipeRefreshPS.setOnRefreshListener(this::loadData); } /** @@ -137,20 +136,20 @@ public class ProductSupplierFragment extends Fragment switch (resource.status) { case LOADING: // Show loading indicator - if (swipeRefresh != null) swipeRefresh.setRefreshing(true); + binding.swipeRefreshPS.setRefreshing(true); break; case SUCCESS: // Hide loading indicator and display data - if (swipeRefresh != null) swipeRefresh.setRefreshing(false); + binding.swipeRefreshPS.setRefreshing(false); if (resource.data != null) { psList.clear(); psList.addAll(resource.data.getContent()); - filter(etSearch != null ? etSearch.getText().toString() : ""); + filter(binding.etSearchPS != null ? binding.etSearchPS.getText().toString() : ""); } break; case ERROR: // Hide loading indicator and toast error message - if (swipeRefresh != null) swipeRefresh.setRefreshing(false); + binding.swipeRefreshPS.setRefreshing(false); Toast.makeText(getContext(), "Failed to load: " + resource.message, Toast.LENGTH_SHORT).show(); Log.e("PSFragment", "Error loading: " + resource.message); break; diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/PurchaseOrderFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/PurchaseOrderFragment.java index 36050661..078ef06c 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/PurchaseOrderFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/PurchaseOrderFragment.java @@ -11,14 +11,14 @@ 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.databinding.FragmentPurchaseOrderBinding; 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 dagger.hilt.android.AndroidEntryPoint; @@ -27,11 +27,10 @@ import dagger.hilt.android.AndroidEntryPoint; public class PurchaseOrderFragment extends Fragment implements PurchaseOrderAdapter.OnPurchaseOrderClickListener { + private FragmentPurchaseOrderBinding binding; private List poList = new ArrayList<>(); private List filteredList = new ArrayList<>(); private PurchaseOrderAdapter adapter; - private SwipeRefreshLayout swipeRefresh; - private EditText etSearch; private PurchaseOrderViewModel viewModel; /** @@ -47,17 +46,16 @@ public class PurchaseOrderFragment extends Fragment * Sets up the fragment's UI components, including RecyclerView, search, and swipe-to-refresh. */ @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_purchase_order, container, false); + binding = FragmentPurchaseOrderBinding.inflate(inflater, container, false); - setupRecyclerView(view); - setupSearch(view); - setupSwipeRefresh(view); + setupRecyclerView(); + setupSearch(); + setupSwipeRefresh(); loadData(); - ImageButton hamburger = view.findViewById(R.id.btnHamburgerPO); - hamburger.setOnClickListener(v -> { + binding.btnHamburgerPO.setOnClickListener(v -> { Fragment parent = getParentFragment(); if (parent != null) { Fragment grandParent = parent.getParentFragment(); @@ -67,25 +65,29 @@ public class PurchaseOrderFragment extends Fragment } }); - return view; + return binding.getRoot(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; } /** * Initializes the RecyclerView with a layout manager and adapter for purchase orders. */ - private void setupRecyclerView(View view) { - RecyclerView rv = view.findViewById(R.id.recyclerViewPO); + private void setupRecyclerView() { adapter = new PurchaseOrderAdapter(filteredList, this); - rv.setLayoutManager(new LinearLayoutManager(getContext())); - rv.setAdapter(adapter); + binding.recyclerViewPO.setLayoutManager(new LinearLayoutManager(getContext())); + binding.recyclerViewPO.setAdapter(adapter); } /** * Configures the search bar for filtering. */ - private void setupSearch(View view) { - etSearch = view.findViewById(R.id.etSearchPO); - etSearch.addTextChangedListener(new TextWatcher() { + private void setupSearch() { + binding.etSearchPO.addTextChangedListener(new TextWatcher() { public void beforeTextChanged(CharSequence s, int a, int b, int c) { } @@ -101,9 +103,8 @@ public class PurchaseOrderFragment extends Fragment /** * Sets up the SwipeRefreshLayout to allow manual reloading of purchase order data. */ - private void setupSwipeRefresh(View view) { - swipeRefresh = view.findViewById(R.id.swipeRefreshPO); - swipeRefresh.setOnRefreshListener(this::loadData); + private void setupSwipeRefresh() { + binding.swipeRefreshPO.setOnRefreshListener(this::loadData); } /** @@ -137,20 +138,20 @@ public class PurchaseOrderFragment extends Fragment switch (resource.status) { case LOADING: // Show loading indicator - if (swipeRefresh != null) swipeRefresh.setRefreshing(true); + binding.swipeRefreshPO.setRefreshing(true); break; case SUCCESS: // Hide loading indicator and display data - if (swipeRefresh != null) swipeRefresh.setRefreshing(false); + binding.swipeRefreshPO.setRefreshing(false); if (resource.data != null) { poList.clear(); poList.addAll(resource.data.getContent()); - filter(etSearch != null ? etSearch.getText().toString() : ""); + filter(binding.etSearchPO != null ? binding.etSearchPO.getText().toString() : ""); } break; case ERROR: // Hide loading indicator and toast error message - if (swipeRefresh != null) swipeRefresh.setRefreshing(false); + binding.swipeRefreshPO.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; diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/SaleFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/SaleFragment.java index 859bacca..99f249db 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/SaleFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/SaleFragment.java @@ -2,23 +2,19 @@ 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; -import androidx.recyclerview.widget.RecyclerView; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import android.text.Editable; import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.EditText; -import android.widget.ImageButton; import com.example.petstoremobile.R; import com.example.petstoremobile.adapters.SaleAdapter; import com.example.petstoremobile.api.SaleApi; +import com.example.petstoremobile.databinding.FragmentSaleBinding; import com.example.petstoremobile.fragments.ListFragment; import com.example.petstoremobile.models.Sale; import java.util.ArrayList; @@ -31,46 +27,45 @@ import dagger.hilt.android.AndroidEntryPoint; @AndroidEntryPoint public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickListener { + private FragmentSaleBinding binding; private List saleList = new ArrayList<>(); private List filteredList = new ArrayList<>(); private SaleAdapter adapter; - private SwipeRefreshLayout swipeRefreshLayout; - private EditText etSearch; - private ImageButton btnHamburger; @Inject SaleApi api; @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_sale, container, false); + binding = FragmentSaleBinding.inflate(inflater, container, false); - btnHamburger = view.findViewById(R.id.btnHamburger); - - setupRecyclerView(view); + setupRecyclerView(); loadSaleData(); - setupSearch(view); - setupSwipeRefresh(view); + setupSearch(); + setupSwipeRefresh(); // Make the hamburger button open the drawer from listFragment - if (btnHamburger != null) { - btnHamburger.setOnClickListener(v -> { - Fragment parent = getParentFragment(); - if (parent != null) { - Fragment grandParent = parent.getParentFragment(); - if (grandParent instanceof ListFragment) { - ((ListFragment) grandParent).openDrawer(); - } + binding.btnHamburger.setOnClickListener(v -> { + Fragment parent = getParentFragment(); + if (parent != null) { + Fragment grandParent = parent.getParentFragment(); + if (grandParent instanceof ListFragment) { + ((ListFragment) grandParent).openDrawer(); } - }); - } + } + }); - return view; + return binding.getRoot(); } - private void setupSearch(View view) { - etSearch = view.findViewById(R.id.etSearchSale); - etSearch.addTextChangedListener(new TextWatcher() { + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; + } + + private void setupSearch() { + binding.etSearchSale.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @@ -104,11 +99,10 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis adapter.notifyDataSetChanged(); } - private void setupSwipeRefresh(View view) { - swipeRefreshLayout = view.findViewById(R.id.swipeRefreshSale); - swipeRefreshLayout.setOnRefreshListener(() -> { + private void setupSwipeRefresh() { + binding.swipeRefreshSale.setOnRefreshListener(() -> { loadSaleData(); - swipeRefreshLayout.setRefreshing(false); + binding.swipeRefreshSale.setRefreshing(false); }); } @@ -143,10 +137,9 @@ public class SaleFragment extends Fragment implements SaleAdapter.OnSaleClickLis adapter.notifyDataSetChanged(); } - private void setupRecyclerView(View view) { - RecyclerView recyclerView = view.findViewById(R.id.recyclerViewSales); + private void setupRecyclerView() { adapter = new SaleAdapter(filteredList, this); - recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); - recyclerView.setAdapter(adapter); + binding.recyclerViewSales.setLayoutManager(new LinearLayoutManager(getContext())); + binding.recyclerViewSales.setAdapter(adapter); } } diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ServiceFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ServiceFragment.java index ff0819b2..8ae3c5a5 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ServiceFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ServiceFragment.java @@ -8,8 +8,6 @@ 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 android.text.Editable; import android.text.TextWatcher; @@ -17,17 +15,14 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.EditText; -import android.widget.ImageButton; import android.widget.Toast; import com.example.petstoremobile.R; import com.example.petstoremobile.adapters.ServiceAdapter; +import com.example.petstoremobile.databinding.FragmentServiceBinding; 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; @@ -37,13 +32,11 @@ import dagger.hilt.android.AndroidEntryPoint; @AndroidEntryPoint public class ServiceFragment extends Fragment implements ServiceAdapter.OnServiceClickListener { + private FragmentServiceBinding binding; private List serviceList = new ArrayList<>(); private List filteredList = new ArrayList<>(); private ServiceAdapter adapter; - private ImageButton hamburger; private ServiceViewModel viewModel; - private SwipeRefreshLayout swipeRefreshLayout; - private EditText etSearch; /** * Initializes the fragment and its associated ServiceViewModel. @@ -60,21 +53,18 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_service, container, false); + binding = FragmentServiceBinding.inflate(inflater, container, false); - hamburger = view.findViewById(R.id.btnHamburger); - - setupRecyclerView(view); - setupSearch(view); - setupSwipeRefresh(view); + setupRecyclerView(); + setupSearch(); + setupSwipeRefresh(); loadServiceData(); //Add button to opens the add dialog - FloatingActionButton fabAddService = view.findViewById(R.id.fabAddService); - fabAddService.setOnClickListener(v -> openServiceDetails(-1)); + binding.fabAddService.setOnClickListener(v -> openServiceDetails(-1)); //Make the hamburger button open the drawer from listFragment - hamburger.setOnClickListener(v -> { + binding.btnHamburger.setOnClickListener(v -> { Fragment parent = getParentFragment(); if (parent != null) { Fragment grandParent = parent.getParentFragment(); @@ -84,15 +74,20 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic } }); - return view; + return binding.getRoot(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; } /** * Configures the search bar for filtering. */ - private void setupSearch(View view) { - etSearch = view.findViewById(R.id.etSearchService); - etSearch.addTextChangedListener(new TextWatcher() { + private void setupSearch() { + binding.etSearchService.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void onTextChanged(CharSequence s, int start, int before, int count) { filterServices(s.toString()); @@ -123,9 +118,8 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic /** * Sets up the SwipeRefreshLayout to allow manual reloading of service data. */ - private void setupSwipeRefresh(View view) { - swipeRefreshLayout = view.findViewById(R.id.swipeRefreshService); - swipeRefreshLayout.setOnRefreshListener(this::loadServiceData); + private void setupSwipeRefresh() { + binding.swipeRefreshService.setOnRefreshListener(this::loadServiceData); } /** @@ -169,20 +163,20 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic switch (resource.status) { case LOADING: // Show loading indicator - if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(true); + binding.swipeRefreshService.setRefreshing(true); break; case SUCCESS: // Hide loading indicator and display data - if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false); + binding.swipeRefreshService.setRefreshing(false); if (resource.data != null) { serviceList.clear(); serviceList.addAll(resource.data.getContent()); - filterServices(etSearch != null ? etSearch.getText().toString() : ""); + filterServices(binding.etSearchService != null ? binding.etSearchService.getText().toString() : ""); } break; case ERROR: // Hide loading indicator and toast error message - if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false); + binding.swipeRefreshService.setRefreshing(false); if (getContext() != null) { Toast.makeText(getContext(), "Failed to load services: " + resource.message, Toast.LENGTH_SHORT).show(); } @@ -195,10 +189,9 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic /** * Initializes the RecyclerView with a layout manager and adapter for services. */ - private void setupRecyclerView(View view) { - RecyclerView recyclerView = view.findViewById(R.id.recyclerViewServices); + private void setupRecyclerView() { adapter = new ServiceAdapter(filteredList, this); - recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); - recyclerView.setAdapter(adapter); + binding.recyclerViewServices.setLayoutManager(new LinearLayoutManager(getContext())); + binding.recyclerViewServices.setAdapter(adapter); } } diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/SupplierFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/SupplierFragment.java index baa67b5b..4018c27a 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/SupplierFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/SupplierFragment.java @@ -8,8 +8,6 @@ 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 android.text.Editable; import android.text.TextWatcher; @@ -17,17 +15,14 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.EditText; -import android.widget.ImageButton; import android.widget.Toast; import com.example.petstoremobile.R; import com.example.petstoremobile.adapters.SupplierAdapter; +import com.example.petstoremobile.databinding.FragmentSupplierBinding; 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; @@ -37,13 +32,11 @@ import dagger.hilt.android.AndroidEntryPoint; @AndroidEntryPoint public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupplierClickListener { + private FragmentSupplierBinding binding; private List supplierList = new ArrayList<>(); private List filteredList = new ArrayList<>(); private SupplierAdapter adapter; - private ImageButton hamburger; private SupplierViewModel viewModel; - private SwipeRefreshLayout swipeRefreshLayout; - private EditText etSearch; /** * Initializes the fragment and its associated SupplierViewModel. @@ -60,21 +53,18 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_supplier, container, false); + binding = FragmentSupplierBinding.inflate(inflater, container, false); - hamburger = view.findViewById(R.id.btnHamburger); - - setupRecyclerView(view); - setupSearch(view); - setupSwipeRefresh(view); + setupRecyclerView(); + setupSearch(); + setupSwipeRefresh(); loadSupplierData(); //Add button to opens the add dialog - FloatingActionButton fabAddSupplier = view.findViewById(R.id.fabAddSupplier); - fabAddSupplier.setOnClickListener(v -> openSupplierDetails(-1)); + binding.fabAddSupplier.setOnClickListener(v -> openSupplierDetails(-1)); //Make the hamburger button open the drawer from listFragment - hamburger.setOnClickListener(v -> { + binding.btnHamburger.setOnClickListener(v -> { Fragment parent = getParentFragment(); if (parent != null) { Fragment grandParent = parent.getParentFragment(); @@ -84,15 +74,20 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp } }); - return view; + return binding.getRoot(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; } /** * Configures the search bar for filtering. */ - private void setupSearch(View view) { - etSearch = view.findViewById(R.id.etSearchSupplier); - etSearch.addTextChangedListener(new TextWatcher() { + private void setupSearch() { + binding.etSearchSupplier.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void onTextChanged(CharSequence s, int start, int before, int count) { filterSuppliers(s.toString()); @@ -124,9 +119,8 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp /** * Sets up the SwipeRefreshLayout to allow manual reloading of supplier data. */ - private void setupSwipeRefresh(View view) { - swipeRefreshLayout = view.findViewById(R.id.swipeRefreshSupplier); - swipeRefreshLayout.setOnRefreshListener(this::loadSupplierData); + private void setupSwipeRefresh() { + binding.swipeRefreshSupplier.setOnRefreshListener(this::loadSupplierData); } /** @@ -172,20 +166,20 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp switch (resource.status) { case LOADING: // Show loading indicator - if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(true); + binding.swipeRefreshSupplier.setRefreshing(true); break; case SUCCESS: // Hide loading indicator and display data - if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false); + binding.swipeRefreshSupplier.setRefreshing(false); if (resource.data != null) { supplierList.clear(); supplierList.addAll(resource.data.getContent()); - filterSuppliers(etSearch.getText().toString()); + filterSuppliers(binding.etSearchSupplier.getText().toString()); } break; case ERROR: // Hide loading indicator and toast error message - if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false); + binding.swipeRefreshSupplier.setRefreshing(false); if (getContext() != null) { Toast.makeText(getContext(), "Failed to load suppliers: " + resource.message, Toast.LENGTH_SHORT).show(); } @@ -198,10 +192,9 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp /** * Initializes the RecyclerView with a layout manager and adapter for displaying suppliers. */ - private void setupRecyclerView(View view) { - RecyclerView recyclerView = view.findViewById(R.id.recyclerViewSuppliers); + private void setupRecyclerView() { adapter = new SupplierAdapter(filteredList, this); - recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); - recyclerView.setAdapter(adapter); + binding.recyclerViewSuppliers.setLayoutManager(new LinearLayoutManager(getContext())); + binding.recyclerViewSuppliers.setAdapter(adapter); } } diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AdoptionDetailFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AdoptionDetailFragment.java index 2aa140bf..3702e168 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AdoptionDetailFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AdoptionDetailFragment.java @@ -2,7 +2,6 @@ package com.example.petstoremobile.fragments.listfragments.detailfragments; import android.app.DatePickerDialog; import android.os.Bundle; -import android.util.Log; import android.view.*; import android.widget.*; import androidx.annotation.NonNull; @@ -10,7 +9,7 @@ import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.navigation.fragment.NavHostFragment; -import com.example.petstoremobile.R; +import com.example.petstoremobile.databinding.FragmentAdoptionDetailBinding; import com.example.petstoremobile.dtos.*; import com.example.petstoremobile.utils.DialogUtils; import com.example.petstoremobile.utils.Resource; @@ -29,10 +28,7 @@ import dagger.hilt.android.AndroidEntryPoint; @AndroidEntryPoint public class AdoptionDetailFragment extends Fragment { - private TextView tvMode, tvAdoptionId; - private EditText etAdoptionDate; - private Spinner spinnerPet, spinnerCustomer, spinnerStatus; - private Button btnSave, btnDelete, btnBack; + private FragmentAdoptionDetailBinding binding; private long adoptionId = -1; private boolean isEditing = false; @@ -59,49 +55,39 @@ public class AdoptionDetailFragment extends Fragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_adoption_detail, container, false); - initViews(view); + binding = FragmentAdoptionDetailBinding.inflate(inflater, container, false); setupSpinners(); setupDatePicker(); loadData(); handleArguments(); - btnBack.setOnClickListener(v -> navigateBack()); - btnSave.setOnClickListener(v -> saveAdoption()); - btnDelete.setOnClickListener(v -> confirmDelete()); - return view; + binding.btnAdoptionBack.setOnClickListener(v -> navigateBack()); + binding.btnSaveAdoption.setOnClickListener(v -> saveAdoption()); + binding.btnDeleteAdoption.setOnClickListener(v -> confirmDelete()); + return binding.getRoot(); } - /** - * Initializes UI components from the layout. - */ - private void initViews(View v) { - tvMode = v.findViewById(R.id.tvAdoptionMode); - tvAdoptionId = v.findViewById(R.id.tvAdoptionId); - etAdoptionDate = v.findViewById(R.id.etAdoptionDate); - spinnerPet = v.findViewById(R.id.spinnerAdoptionPet); - spinnerCustomer= v.findViewById(R.id.spinnerAdoptionCustomer); - spinnerStatus = v.findViewById(R.id.spinnerAdoptionStatus); - btnSave = v.findViewById(R.id.btnSaveAdoption); - btnDelete = v.findViewById(R.id.btnDeleteAdoption); - btnBack = v.findViewById(R.id.btnAdoptionBack); + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; } /** * Configures the spinner for adoption status. */ private void setupSpinners() { - SpinnerUtils.setupStringSpinner(requireContext(), spinnerStatus, STATUSES); + SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerAdoptionStatus, STATUSES); } /** * Configures the date picker dialog for the adoption date field. */ private void setupDatePicker() { - etAdoptionDate.setOnClickListener(v -> { + binding.etAdoptionDate.setOnClickListener(v -> { Calendar c = Calendar.getInstance(); new DatePickerDialog(requireContext(), - (dp, y, m, d) -> etAdoptionDate.setText( + (dp, y, m, d) -> binding.etAdoptionDate.setText( String.format("%04d-%02d-%02d", y, m + 1, d)), c.get(Calendar.YEAR), c.get(Calendar.MONTH), @@ -124,7 +110,7 @@ public class AdoptionDetailFragment extends Fragment { petViewModel.getAllPets(0, 200).observe(getViewLifecycleOwner(), resource -> { if (resource.status == Resource.Status.SUCCESS && resource.data != null) { petList = resource.data.getContent(); - SpinnerUtils.populateSpinner(requireContext(), spinnerPet, petList, + SpinnerUtils.populateSpinner(requireContext(), binding.spinnerAdoptionPet, petList, PetDTO::getPetName, "-- Select Pet --", preselectedPetId, PetDTO::getPetId); } @@ -138,7 +124,7 @@ public class AdoptionDetailFragment extends Fragment { customerViewModel.getAllCustomers(0, 200).observe(getViewLifecycleOwner(), resource -> { if (resource.status == Resource.Status.SUCCESS && resource.data != null) { customerList = resource.data.getContent(); - SpinnerUtils.populateSpinner(requireContext(), spinnerCustomer, customerList, + SpinnerUtils.populateSpinner(requireContext(), binding.spinnerAdoptionCustomer, customerList, item -> item.getFirstName() + " " + item.getLastName(), "-- Select Customer --", preselectedCustomerId, CustomerDTO::getCustomerId); @@ -157,18 +143,18 @@ public class AdoptionDetailFragment extends Fragment { preselectedPetId = a.getLong("petId", -1); preselectedCustomerId = a.getLong("customerId", -1); - tvMode.setText("Edit Adoption"); - tvAdoptionId.setText("ID: " + adoptionId); - tvAdoptionId.setVisibility(View.VISIBLE); - etAdoptionDate.setText(a.getString("adoptionDate")); - btnDelete.setVisibility(View.VISIBLE); + binding.tvAdoptionMode.setText("Edit Adoption"); + binding.tvAdoptionId.setText("ID: " + adoptionId); + binding.tvAdoptionId.setVisibility(View.VISIBLE); + binding.etAdoptionDate.setText(a.getString("adoptionDate")); + binding.btnDeleteAdoption.setVisibility(View.VISIBLE); // Pre-fill status - SpinnerUtils.setSelectionByValue(spinnerStatus, a.getString("adoptionStatus", "Pending")); + SpinnerUtils.setSelectionByValue(binding.spinnerAdoptionStatus, a.getString("adoptionStatus", "Pending")); } else { - tvMode.setText("Add Adoption"); - btnDelete.setVisibility(View.GONE); - tvAdoptionId.setVisibility(View.GONE); + binding.tvAdoptionMode.setText("Add Adoption"); + binding.btnDeleteAdoption.setVisibility(View.GONE); + binding.tvAdoptionId.setVisibility(View.GONE); } } @@ -176,20 +162,20 @@ public class AdoptionDetailFragment extends Fragment { * Validates input and saves the adoption request to the backend. */ private void saveAdoption() { - if (spinnerCustomer.getSelectedItemPosition() == 0) { + if (binding.spinnerAdoptionCustomer.getSelectedItemPosition() == 0) { Toast.makeText(getContext(), "Select a customer", Toast.LENGTH_SHORT).show(); return; } - if (spinnerPet.getSelectedItemPosition() == 0) { + if (binding.spinnerAdoptionPet.getSelectedItemPosition() == 0) { Toast.makeText(getContext(), "Select a pet", Toast.LENGTH_SHORT).show(); return; } - String date = etAdoptionDate.getText().toString().trim(); + String date = binding.etAdoptionDate.getText().toString().trim(); if (date.isEmpty()) { Toast.makeText(getContext(), "Select a date", Toast.LENGTH_SHORT).show(); return; } - CustomerDTO customer = customerList.get(spinnerCustomer.getSelectedItemPosition() - 1); - PetDTO pet = petList.get(spinnerPet.getSelectedItemPosition() - 1); - String status = STATUSES[spinnerStatus.getSelectedItemPosition()]; + CustomerDTO customer = customerList.get(binding.spinnerAdoptionCustomer.getSelectedItemPosition() - 1); + PetDTO pet = petList.get(binding.spinnerAdoptionPet.getSelectedItemPosition() - 1); + String status = STATUSES[binding.spinnerAdoptionStatus.getSelectedItemPosition()]; AdoptionDTO dto = new AdoptionDTO( pet.getPetId(), diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AppointmentDetailFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AppointmentDetailFragment.java index f137bdfd..83e70852 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AppointmentDetailFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AppointmentDetailFragment.java @@ -10,7 +10,7 @@ import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.navigation.fragment.NavHostFragment; -import com.example.petstoremobile.R; +import com.example.petstoremobile.databinding.FragmentAppointmentDetailBinding; import com.example.petstoremobile.dtos.*; import com.example.petstoremobile.utils.DialogUtils; import com.example.petstoremobile.utils.Resource; @@ -31,11 +31,7 @@ import dagger.hilt.android.AndroidEntryPoint; @AndroidEntryPoint public class AppointmentDetailFragment extends Fragment { - private TextView tvMode, tvAppointmentId; - private EditText etAppointmentDate; - private Spinner spinnerPet, spinnerService, spinnerStatus, spinnerHour, spinnerMinute; - private Spinner spinnerCustomer, spinnerStore; - private Button btnSave, btnDelete, btnBack; + private FragmentAppointmentDetailBinding binding; private long appointmentId = -1; private boolean isEditing = false; @@ -71,59 +67,45 @@ public class AppointmentDetailFragment extends Fragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_appointment_detail, container, false); - initViews(view); + binding = FragmentAppointmentDetailBinding.inflate(inflater, container, false); setupSpinners(); setupDatePicker(); loadData(); handleArguments(); - btnBack.setOnClickListener(v -> navigateBack()); - btnSave.setOnClickListener(v -> saveAppointment()); - btnDelete.setOnClickListener(v -> confirmDelete()); - return view; + binding.btnApptBack.setOnClickListener(v -> navigateBack()); + binding.btnSaveAppointment.setOnClickListener(v -> saveAppointment()); + binding.btnDeleteAppointment.setOnClickListener(v -> confirmDelete()); + return binding.getRoot(); } - /** - * Initializes UI components from the layout. - */ - private void initViews(View v) { - tvMode = v.findViewById(R.id.tvApptMode); - tvAppointmentId = v.findViewById(R.id.tvAppointmentId); - etAppointmentDate= v.findViewById(R.id.etAppointmentDate); - spinnerPet = v.findViewById(R.id.spinnerPet); - spinnerService = v.findViewById(R.id.spinnerService); - spinnerStatus = v.findViewById(R.id.spinnerAppointmentStatus); - spinnerHour = v.findViewById(R.id.spinnerHour); - spinnerMinute = v.findViewById(R.id.spinnerMinute); - spinnerCustomer = v.findViewById(R.id.spinnerCustomer); - spinnerStore = v.findViewById(R.id.spinnerStore); - btnSave = v.findViewById(R.id.btnSaveAppointment); - btnDelete = v.findViewById(R.id.btnDeleteAppointment); - btnBack = v.findViewById(R.id.btnApptBack); + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; } /** * Configures the adapters for spinners. */ private void setupSpinners() { - SpinnerUtils.setupStringSpinner(requireContext(), spinnerStatus, STATUSES); + SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerAppointmentStatus, STATUSES); String[] hours = new String[HOURS.length]; for (int i = 0; i < HOURS.length; i++) hours[i] = String.format("%02d:00", HOURS[i]); - SpinnerUtils.setupStringSpinner(requireContext(), spinnerHour, hours); - SpinnerUtils.setupStringSpinner(requireContext(), spinnerMinute, new String[]{"00","15","30","45"}); + SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerHour, hours); + SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerMinute, new String[]{"00","15","30","45"}); } /** * Configures the date picker dialog for the appointment date field. */ private void setupDatePicker() { - etAppointmentDate.setOnClickListener(v -> { + binding.etAppointmentDate.setOnClickListener(v -> { Calendar c = Calendar.getInstance(); DatePickerDialog d = new DatePickerDialog(requireContext(), - (dp,y,m,d1) -> etAppointmentDate.setText( + (dp,y,m,d1) -> binding.etAppointmentDate.setText( String.format("%04d-%02d-%02d", y, m+1, d1)), c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH)); @@ -149,7 +131,7 @@ public class AppointmentDetailFragment extends Fragment { petViewModel.getAllPets(0, 200).observe(getViewLifecycleOwner(), resource -> { if (resource.status == Resource.Status.SUCCESS && resource.data != null) { petList = resource.data.getContent(); - SpinnerUtils.populateSpinner(requireContext(), spinnerPet, petList, + SpinnerUtils.populateSpinner(requireContext(), binding.spinnerPet, petList, PetDTO::getPetName, "-- Select Pet --", preselectedPetId, PetDTO::getPetId); } @@ -163,7 +145,7 @@ public class AppointmentDetailFragment extends Fragment { serviceViewModel.getAllServices(0, 200).observe(getViewLifecycleOwner(), resource -> { if (resource.status == Resource.Status.SUCCESS && resource.data != null) { serviceList = resource.data.getContent(); - SpinnerUtils.populateSpinner(requireContext(), spinnerService, serviceList, + SpinnerUtils.populateSpinner(requireContext(), binding.spinnerService, serviceList, ServiceDTO::getServiceName, "-- Select Service --", preselectedServiceId, ServiceDTO::getServiceId); } @@ -177,7 +159,7 @@ public class AppointmentDetailFragment extends Fragment { customerViewModel.getAllCustomers(0, 200).observe(getViewLifecycleOwner(), resource -> { if (resource.status == Resource.Status.SUCCESS && resource.data != null) { customerList = resource.data.getContent(); - SpinnerUtils.populateSpinner(requireContext(), spinnerCustomer, customerList, + SpinnerUtils.populateSpinner(requireContext(), binding.spinnerCustomer, customerList, item -> item.getFirstName() + " " + item.getLastName(), "-- Select Customer --", preselectedCustomerId, CustomerDTO::getCustomerId); @@ -192,7 +174,7 @@ public class AppointmentDetailFragment extends Fragment { storeViewModel.getAllStores(0, 50).observe(getViewLifecycleOwner(), resource -> { if (resource.status == Resource.Status.SUCCESS && resource.data != null) { storeList = resource.data.getContent(); - SpinnerUtils.populateSpinner(requireContext(), spinnerStore, storeList, + SpinnerUtils.populateSpinner(requireContext(), binding.spinnerStore, storeList, StoreDTO::getStoreName, "-- Select Store --", preselectedStoreId, StoreDTO::getStoreId); } @@ -212,11 +194,11 @@ public class AppointmentDetailFragment extends Fragment { preselectedCustomerId = a.getLong("customerId", -1); preselectedStoreId = a.getLong("storeId", -1); - tvMode.setText("Edit Appointment"); - tvAppointmentId.setText("ID: " + appointmentId); - tvAppointmentId.setVisibility(View.VISIBLE); - etAppointmentDate.setText(a.getString("appointmentDate")); - btnDelete.setVisibility(View.VISIBLE); + binding.tvApptMode.setText("Edit Appointment"); + binding.tvAppointmentId.setText("ID: " + appointmentId); + binding.tvAppointmentId.setVisibility(View.VISIBLE); + binding.etAppointmentDate.setText(a.getString("appointmentDate")); + binding.btnDeleteAppointment.setVisibility(View.VISIBLE); // Pre-fill time spinners String time = a.getString("appointmentTime", "09:00"); @@ -226,18 +208,18 @@ public class AppointmentDetailFragment extends Fragment { int hour = Integer.parseInt(parts[0]); int min = Integer.parseInt(parts[1]); for (int i = 0; i < HOURS.length; i++) - if (HOURS[i] == hour) { spinnerHour.setSelection(i); break; } + if (HOURS[i] == hour) { binding.spinnerHour.setSelection(i); break; } for (int i = 0; i < MINUTES.length; i++) - if (MINUTES[i] == min) { spinnerMinute.setSelection(i); break; } + if (MINUTES[i] == min) { binding.spinnerMinute.setSelection(i); break; } } // Pre-fill status - SpinnerUtils.setSelectionByValue(spinnerStatus, a.getString("appointmentStatus", "Booked")); + SpinnerUtils.setSelectionByValue(binding.spinnerAppointmentStatus, a.getString("appointmentStatus", "Booked")); } else { - tvMode.setText("Add Appointment"); - btnDelete.setVisibility(View.GONE); - tvAppointmentId.setVisibility(View.GONE); + binding.tvApptMode.setText("Add Appointment"); + binding.btnDeleteAppointment.setVisibility(View.GONE); + binding.tvAppointmentId.setVisibility(View.GONE); } } @@ -245,32 +227,32 @@ public class AppointmentDetailFragment extends Fragment { * Validates input and saves the appointment to the backend. */ private void saveAppointment() { - if (spinnerCustomer.getSelectedItemPosition() == 0) { + if (binding.spinnerCustomer.getSelectedItemPosition() == 0) { Toast.makeText(getContext(), "Select a customer", Toast.LENGTH_SHORT).show(); return; } - if (spinnerStore.getSelectedItemPosition() == 0) { + if (binding.spinnerStore.getSelectedItemPosition() == 0) { Toast.makeText(getContext(), "Select a store", Toast.LENGTH_SHORT).show(); return; } - if (spinnerPet.getSelectedItemPosition() == 0) { + if (binding.spinnerPet.getSelectedItemPosition() == 0) { Toast.makeText(getContext(), "Select a pet", Toast.LENGTH_SHORT).show(); return; } - if (spinnerService.getSelectedItemPosition() == 0) { + if (binding.spinnerService.getSelectedItemPosition() == 0) { Toast.makeText(getContext(), "Select a service", Toast.LENGTH_SHORT).show(); return; } - String date = etAppointmentDate.getText().toString().trim(); + String date = binding.etAppointmentDate.getText().toString().trim(); if (date.isEmpty()) { Toast.makeText(getContext(), "Select a date", Toast.LENGTH_SHORT).show(); return; } - CustomerDTO customer = customerList.get(spinnerCustomer.getSelectedItemPosition() - 1); - StoreDTO store = storeList.get(spinnerStore.getSelectedItemPosition() - 1); - PetDTO pet = petList.get(spinnerPet.getSelectedItemPosition() - 1); - ServiceDTO service = serviceList.get(spinnerService.getSelectedItemPosition() - 1); + CustomerDTO customer = customerList.get(binding.spinnerCustomer.getSelectedItemPosition() - 1); + StoreDTO store = storeList.get(binding.spinnerStore.getSelectedItemPosition() - 1); + PetDTO pet = petList.get(binding.spinnerPet.getSelectedItemPosition() - 1); + ServiceDTO service = serviceList.get(binding.spinnerService.getSelectedItemPosition() - 1); String time = String.format("%02d:%02d", - HOURS[spinnerHour.getSelectedItemPosition()], - MINUTES[spinnerMinute.getSelectedItemPosition()]); - String status = STATUSES[spinnerStatus.getSelectedItemPosition()]; + HOURS[binding.spinnerHour.getSelectedItemPosition()], + MINUTES[binding.spinnerMinute.getSelectedItemPosition()]); + String status = STATUSES[binding.spinnerAppointmentStatus.getSelectedItemPosition()]; // Validate future date+time if status is Booked diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/InventoryDetailFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/InventoryDetailFragment.java index e2b0984d..bafa71c5 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/InventoryDetailFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/InventoryDetailFragment.java @@ -9,9 +9,6 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; -import android.widget.AutoCompleteTextView; -import android.widget.Button; -import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; @@ -20,9 +17,8 @@ import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.navigation.fragment.NavHostFragment; -import com.example.petstoremobile.R; import com.example.petstoremobile.adapters.BlackTextArrayAdapter; -import com.example.petstoremobile.dtos.InventoryDTO; +import com.example.petstoremobile.databinding.FragmentInventoryDetailBinding; import com.example.petstoremobile.dtos.InventoryRequest; import com.example.petstoremobile.dtos.ProductDTO; import com.example.petstoremobile.utils.InputValidator; @@ -41,10 +37,7 @@ import dagger.hilt.android.AndroidEntryPoint; @AndroidEntryPoint public class InventoryDetailFragment extends Fragment { - private TextView tvMode, tvInventoryId, tvProductInfo; - private AutoCompleteTextView etProductSearch; - private android.widget.EditText etQuantity; - private Button btnSave, btnDelete, btnBack; + private FragmentInventoryDetailBinding binding; private InventoryViewModel inventoryViewModel; private ProductViewModel productViewModel; @@ -73,44 +66,35 @@ public class InventoryDetailFragment extends Fragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_inventory_detail, container, false); + binding = FragmentInventoryDetailBinding.inflate(inflater, container, false); - initViews(view); setupProductSearch(); handleArguments(); - btnBack.setOnClickListener(v -> navigateBack()); - btnSave.setOnClickListener(v -> saveInventory()); - btnDelete.setOnClickListener(v -> confirmDelete()); - - return view; - } - - /** - * get the layout view and set adapter. - */ - private void initViews(View view) { - tvMode = view.findViewById(R.id.tvInventoryMode); - tvInventoryId = view.findViewById(R.id.tvInventoryId); - tvProductInfo = view.findViewById(R.id.tvProductInfo); - etProductSearch = view.findViewById(R.id.etProductSearch); - etQuantity = view.findViewById(R.id.etQuantity); - btnSave = view.findViewById(R.id.btnSaveInventory); - btnDelete = view.findViewById(R.id.btnDeleteInventory); - btnBack = view.findViewById(R.id.btnInventoryBack); + binding.btnInventoryBack.setOnClickListener(v -> navigateBack()); + binding.btnSaveInventory.setOnClickListener(v -> saveInventory()); + binding.btnDeleteInventory.setOnClickListener(v -> confirmDelete()); // Setup dropdown adapter dropdownAdapter = new BlackTextArrayAdapter<>(requireContext(), android.R.layout.simple_dropdown_item_1line, new ArrayList<>()); - etProductSearch.setAdapter(dropdownAdapter); - etProductSearch.setThreshold(1); // start showing after 1 character + binding.etProductSearch.setAdapter(dropdownAdapter); + binding.etProductSearch.setThreshold(1); // start showing after 1 character + + return binding.getRoot(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; } /** * setup the product search dropdown. */ private void setupProductSearch() { - etProductSearch.addTextChangedListener(new TextWatcher() { + binding.etProductSearch.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int i, int i1, int i2) { } @@ -123,7 +107,7 @@ public class InventoryDetailFragment extends Fragment { public void onTextChanged(CharSequence s, int start, int before, int count) { // Clear selected product when user is typing again selectedProduct = null; - tvProductInfo.setVisibility(View.GONE); + binding.tvProductInfo.setVisibility(View.GONE); if (searchRunnable != null) searchHandler.removeCallbacks(searchRunnable); @@ -137,14 +121,14 @@ public class InventoryDetailFragment extends Fragment { }); // When user picks an item from the dropdown - etProductSearch.setOnItemClickListener((parent, view, position, id) -> { + binding.etProductSearch.setOnItemClickListener((parent, view, position, id) -> { if (position < productSuggestions.size()) { selectedProduct = productSuggestions.get(position); // Show product details below the search box - tvProductInfo.setText( + binding.tvProductInfo.setText( "ID: " + selectedProduct.getProdId() + " • " + selectedProduct.getCategoryName()); - tvProductInfo.setVisibility(View.VISIBLE); + binding.tvProductInfo.setVisibility(View.VISIBLE); } }); } @@ -167,7 +151,7 @@ public class InventoryDetailFragment extends Fragment { dropdownAdapter.clear(); dropdownAdapter.addAll(names); dropdownAdapter.notifyDataSetChanged(); - etProductSearch.showDropDown(); + binding.etProductSearch.showDropDown(); } }); } @@ -181,37 +165,37 @@ public class InventoryDetailFragment extends Fragment { isEditing = true; inventoryId = args.getLong("inventoryId"); - tvMode.setText("Edit Inventory"); - tvInventoryId.setText("Inventory ID: " + inventoryId); - tvInventoryId.setVisibility(View.VISIBLE); + binding.tvInventoryMode.setText("Edit Inventory"); + binding.tvInventoryId.setText("Inventory ID: " + inventoryId); + binding.tvInventoryId.setVisibility(View.VISIBLE); // Pre-fill search box with existing product name String productName = args.getString("productName", ""); long prodId = args.getLong("prodId", -1); - etProductSearch.setText(productName); + binding.etProductSearch.setText(productName); // Show existing product info if (prodId != -1) { - tvProductInfo.setText( + binding.tvProductInfo.setText( "ID: " + prodId + " • " + args.getString("categoryName", "")); - tvProductInfo.setVisibility(View.VISIBLE); + binding.tvProductInfo.setVisibility(View.VISIBLE); // Build a minimal ProductDTO so selectedProduct is not null on save selectedProduct = new ProductDTO(productName, null, null, null); selectedProduct.setProdId(prodId); } - etQuantity.setText(String.valueOf(args.getInt("quantity", 0))); - btnDelete.setVisibility(View.VISIBLE); - btnSave.setText("Save"); + binding.etQuantity.setText(String.valueOf(args.getInt("quantity", 0))); + binding.btnDeleteInventory.setVisibility(View.VISIBLE); + binding.btnSaveInventory.setText("Save"); } else { isEditing = false; - tvMode.setText("Add Inventory"); - tvInventoryId.setVisibility(View.GONE); - tvProductInfo.setVisibility(View.GONE); - btnDelete.setVisibility(View.GONE); - btnSave.setText("Add"); + binding.tvInventoryMode.setText("Add Inventory"); + binding.tvInventoryId.setVisibility(View.GONE); + binding.tvProductInfo.setVisibility(View.GONE); + binding.btnDeleteInventory.setVisibility(View.GONE); + binding.btnSaveInventory.setText("Add"); } } @@ -220,17 +204,17 @@ public class InventoryDetailFragment extends Fragment { */ private void saveInventory() { if (selectedProduct == null) { - etProductSearch.setError("Please select a product from the list"); - etProductSearch.requestFocus(); + binding.etProductSearch.setError("Please select a product from the list"); + binding.etProductSearch.requestFocus(); return; } - if (!InputValidator.isNotEmpty(etQuantity, "Quantity") || - !InputValidator.isPositiveInteger(etQuantity, "Quantity")) { + if (!InputValidator.isNotEmpty(binding.etQuantity, "Quantity") || + !InputValidator.isPositiveInteger(binding.etQuantity, "Quantity")) { return; } - int quantity = Integer.parseInt(etQuantity.getText().toString().trim()); + int quantity = Integer.parseInt(binding.etQuantity.getText().toString().trim()); InventoryRequest request = new InventoryRequest(selectedProduct.getProdId(), quantity); setButtonsEnabled(false); @@ -297,8 +281,8 @@ public class InventoryDetailFragment extends Fragment { * Enables or disables action buttons. */ private void setButtonsEnabled(boolean enabled) { - btnSave.setEnabled(enabled); - btnDelete.setEnabled(enabled); - btnBack.setEnabled(enabled); + binding.btnSaveInventory.setEnabled(enabled); + binding.btnDeleteInventory.setEnabled(enabled); + binding.btnInventoryBack.setEnabled(enabled); } } diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/PetDetailFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/PetDetailFragment.java index 23071db6..9af2b79d 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/PetDetailFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/PetDetailFragment.java @@ -10,13 +10,10 @@ import androidx.navigation.fragment.NavHostFragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.Button; -import android.widget.EditText; -import android.widget.Spinner; -import android.widget.TextView; import android.widget.Toast; import com.example.petstoremobile.R; +import com.example.petstoremobile.databinding.FragmentPetDetailBinding; import com.example.petstoremobile.dtos.PetDTO; import com.example.petstoremobile.utils.ActivityLogger; import com.example.petstoremobile.utils.DialogUtils; @@ -33,10 +30,7 @@ import dagger.hilt.android.AndroidEntryPoint; @AndroidEntryPoint public class PetDetailFragment extends Fragment { - private TextView tvMode, tvPetId; - private EditText etPetName, etPetSpecies, etPetBreed, etPetAge, etPetPrice; - private Spinner spinnerPetStatus; - private Button btnSavePet, btnDeletePet, btnBack; + private FragmentPetDetailBinding binding; private int petId; private boolean isEditing = false; @@ -49,21 +43,25 @@ public class PetDetailFragment extends Fragment { } @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_pet_detail, container, false); + binding = FragmentPetDetailBinding.inflate(inflater, container, false); - //set up spinner and get controls from layout and display the view depending on the mode - initViews(view); setupSpinner(); handleArguments(); //set button click listeners - btnBack.setOnClickListener(v -> navigateBack()); - btnSavePet.setOnClickListener(v -> savePet()); - btnDeletePet.setOnClickListener(v -> deletePet()); + binding.btnBack.setOnClickListener(v -> navigateBack()); + binding.btnSavePet.setOnClickListener(v -> savePet()); + binding.btnDeletePet.setOnClickListener(v -> deletePet()); - return view; + return binding.getRoot(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; } /** @@ -71,19 +69,19 @@ public class PetDetailFragment extends Fragment { */ private void savePet() { // Validates all fields using InputValidator - if (!InputValidator.isNotEmpty(etPetName, "Pet Name")) return; - if (!InputValidator.isNotEmpty(etPetSpecies, "Species")) return; - if (!InputValidator.isNotEmpty(etPetBreed, "Breed")) return; - if (!InputValidator.isPositiveInteger(etPetAge, "Age")) return; - if (!InputValidator.isPositiveDecimal(etPetPrice, "Price")) return; + 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 = etPetName.getText().toString().trim(); - String species = etPetSpecies.getText().toString().trim(); - String breed = etPetBreed.getText().toString().trim(); - int age = Integer.parseInt(etPetAge.getText().toString().trim()); - String priceStr = etPetPrice.getText().toString().trim(); - String status = spinnerPetStatus.getSelectedItem().toString(); + String name = binding.etPetName.getText().toString().trim(); + String species = binding.etPetSpecies.getText().toString().trim(); + String breed = binding.etPetBreed.getText().toString().trim(); + int age = Integer.parseInt(binding.etPetAge.getText().toString().trim()); + String priceStr = binding.etPetPrice.getText().toString().trim(); + String status = binding.spinnerPetStatus.getSelectedItem().toString(); //create a pet object to send to the API PetDTO petDTO = new PetDTO(); @@ -160,48 +158,31 @@ public class PetDetailFragment extends Fragment { // Get pet data from arguments and populate fields isEditing = true; petId = getArguments().getInt("petId"); - tvMode.setText("Edit Pet"); - tvPetId.setText("ID: " + petId); - etPetName.setText(getArguments().getString("petName")); - etPetSpecies.setText(getArguments().getString("petSpecies")); - etPetBreed.setText(getArguments().getString("petBreed")); - etPetAge.setText(String.valueOf(getArguments().getInt("petAge"))); - etPetPrice.setText(String.valueOf(getArguments().getDouble("petPrice"))); - SpinnerUtils.setSelectionByValue(spinnerPetStatus, getArguments().getString("petStatus")); - btnDeletePet.setVisibility(View.VISIBLE); + binding.tvMode.setText("Edit Pet"); + binding.tvPetId.setText("ID: " + petId); + binding.etPetName.setText(getArguments().getString("petName")); + binding.etPetSpecies.setText(getArguments().getString("petSpecies")); + binding.etPetBreed.setText(getArguments().getString("petBreed")); + binding.etPetAge.setText(String.valueOf(getArguments().getInt("petAge"))); + binding.etPetPrice.setText(String.valueOf(getArguments().getDouble("petPrice"))); + SpinnerUtils.setSelectionByValue(binding.spinnerPetStatus, getArguments().getString("petStatus")); + binding.btnDeletePet.setVisibility(View.VISIBLE); } else { // Pet is being added // Set default values for add a new pet isEditing = false; - tvMode.setText("Add Pet"); - tvPetId.setVisibility(View.GONE); - btnDeletePet.setVisibility(View.GONE); - btnSavePet.setText("Add"); + binding.tvMode.setText("Add Pet"); + binding.tvPetId.setVisibility(View.GONE); + binding.btnDeletePet.setVisibility(View.GONE); + binding.btnSavePet.setText("Add"); } } - /** - * Binds UI components from the layout. - */ - private void initViews(View view) { - tvMode = view.findViewById(R.id.tvMode); - tvPetId = view.findViewById(R.id.tvPetId); - etPetName = view.findViewById(R.id.etPetName); - etPetSpecies = view.findViewById(R.id.etPetSpecies); - etPetBreed = view.findViewById(R.id.etPetBreed); - etPetAge = view.findViewById(R.id.etPetAge); - etPetPrice = view.findViewById(R.id.etPetPrice); - spinnerPetStatus = view.findViewById(R.id.spinnerPetStatus); - btnSavePet = view.findViewById(R.id.btnSavePet); - btnDeletePet = view.findViewById(R.id.btnDeletePet); - btnBack = view.findViewById(R.id.btnBack); - } - /** * Initializes the spinner for pet status selection. */ private void setupSpinner() { - SpinnerUtils.setupStringSpinner(requireContext(), spinnerPetStatus, + SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerPetStatus, new String[]{"Available", "Adopted"}); } } diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/ProductDetailFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/ProductDetailFragment.java index 64f2c7f4..4784fd03 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/ProductDetailFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/ProductDetailFragment.java @@ -13,10 +13,10 @@ import com.bumptech.glide.Glide; import com.example.petstoremobile.R; import com.example.petstoremobile.api.*; import com.example.petstoremobile.api.auth.TokenManager; +import com.example.petstoremobile.databinding.FragmentProductDetailBinding; import com.example.petstoremobile.dtos.*; import com.example.petstoremobile.viewmodels.ProductViewModel; import com.example.petstoremobile.utils.DialogUtils; -import com.example.petstoremobile.utils.ErrorUtils; import com.example.petstoremobile.utils.FileUtils; import com.example.petstoremobile.utils.GlideUtils; import com.example.petstoremobile.utils.ImagePickerHelper; @@ -42,11 +42,7 @@ import okhttp3.RequestBody; @AndroidEntryPoint public class ProductDetailFragment extends Fragment { - private TextView tvMode, tvProductId; - private EditText etProductName, etProductDesc, etProductPrice; - private Spinner spinnerCategory; - private Button btnSave, btnDelete, btnBack; - private ImageView ivProductImage; + private FragmentProductDetailBinding binding; private long prodId = -1; private boolean isEditing = false; @@ -75,7 +71,7 @@ public class ProductDetailFragment extends Fragment { @Override public void onImagePicked(Uri uri) { photoUri = uri; - Glide.with(ProductDetailFragment.this).load(uri).into(ivProductImage); + Glide.with(ProductDetailFragment.this).load(uri).into(binding.ivProductImage); hasImage = true; isImageChanged = true; isImageRemoved = false; @@ -87,7 +83,7 @@ public class ProductDetailFragment extends Fragment { hasImage = false; isImageChanged = false; isImageRemoved = true; - ivProductImage.setImageResource(R.drawable.placeholder2); + binding.ivProductImage.setImageResource(R.drawable.placeholder2); } }); } @@ -98,32 +94,22 @@ public class ProductDetailFragment extends Fragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_product_detail, container, false); - initViews(view); + binding = FragmentProductDetailBinding.inflate(inflater, container, false); + loadCategories(); handleArguments(); - btnBack.setOnClickListener(v -> navigateBack()); - btnSave.setOnClickListener(v -> saveProduct()); - btnDelete.setOnClickListener(v -> confirmDelete()); - ivProductImage.setOnClickListener(v -> imagePickerHelper.showImagePickerDialog("Select Product Image", hasImage)); - return view; + binding.btnProductBack.setOnClickListener(v -> navigateBack()); + binding.btnSaveProduct.setOnClickListener(v -> saveProduct()); + binding.btnDeleteProduct.setOnClickListener(v -> confirmDelete()); + binding.ivProductImage.setOnClickListener(v -> imagePickerHelper.showImagePickerDialog("Select Product Image", hasImage)); + return binding.getRoot(); } - /** - * get the UI components from the layout. - */ - private void initViews(View v) { - tvMode = v.findViewById(R.id.tvProductMode); - tvProductId = v.findViewById(R.id.tvProductId); - etProductName = v.findViewById(R.id.etProductName); - etProductDesc = v.findViewById(R.id.etProductDesc); - etProductPrice = v.findViewById(R.id.etProductPrice); - spinnerCategory = v.findViewById(R.id.spinnerProductCategory); - btnSave = v.findViewById(R.id.btnSaveProduct); - btnDelete = v.findViewById(R.id.btnDeleteProduct); - btnBack = v.findViewById(R.id.btnProductBack); - ivProductImage = v.findViewById(R.id.ivProductImage); + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; } /** @@ -133,7 +119,7 @@ public class ProductDetailFragment extends Fragment { viewModel.getAllCategories(0, 100).observe(getViewLifecycleOwner(), resource -> { if (resource != null && resource.status == com.example.petstoremobile.utils.Resource.Status.SUCCESS && resource.data != null) { categoryList = resource.data.getContent(); - SpinnerUtils.populateSpinner(requireContext(), spinnerCategory, categoryList, + SpinnerUtils.populateSpinner(requireContext(), binding.spinnerProductCategory, categoryList, CategoryDTO::getCategoryName, "-- Select Category --", preselectedCategoryId, CategoryDTO::getCategoryId); } @@ -151,18 +137,18 @@ public class ProductDetailFragment extends Fragment { preselectedCategoryId = a.getLong("categoryId", -1); hasImage = true; - tvMode.setText("Edit Product"); - tvProductId.setText("ID: " + prodId); - tvProductId.setVisibility(View.VISIBLE); - etProductName.setText(a.getString("prodName")); - etProductDesc.setText(a.getString("prodDesc")); - etProductPrice.setText(a.getString("prodPrice")); - btnDelete.setVisibility(View.VISIBLE); + binding.tvProductMode.setText("Edit Product"); + binding.tvProductId.setText("ID: " + prodId); + binding.tvProductId.setVisibility(View.VISIBLE); + binding.etProductName.setText(a.getString("prodName")); + binding.etProductDesc.setText(a.getString("prodDesc")); + binding.etProductPrice.setText(a.getString("prodPrice")); + binding.btnDeleteProduct.setVisibility(View.VISIBLE); loadProductImage(); } else { - tvMode.setText("Add Product"); - btnDelete.setVisibility(View.GONE); - tvProductId.setVisibility(View.GONE); + binding.tvProductMode.setText("Add Product"); + binding.btnDeleteProduct.setVisibility(View.GONE); + binding.tvProductId.setVisibility(View.GONE); hasImage = false; } } @@ -174,7 +160,7 @@ public class ProductDetailFragment extends Fragment { String imageUrl = baseUrl + String.format(Locale.US, ProductApi.PRODUCT_IMAGE_PATH, prodId); String token = tokenManager.getToken(); - GlideUtils.loadImageWithToken(requireContext(), ivProductImage, imageUrl, token, R.drawable.placeholder2, new GlideUtils.ImageLoadListener() { + GlideUtils.loadImageWithToken(requireContext(), binding.ivProductImage, imageUrl, token, R.drawable.placeholder2, new GlideUtils.ImageLoadListener() { @Override public void onResourceReady() { hasImage = true; @@ -240,22 +226,22 @@ public class ProductDetailFragment extends Fragment { * Validates input fields and saves product information to the backend. */ private void saveProduct() { - if (!InputValidator.isNotEmpty(etProductName, "Product Name")) return; + if (!InputValidator.isNotEmpty(binding.etProductName, "Product Name")) return; - if (spinnerCategory.getSelectedItemPosition() == 0) { + if (binding.spinnerProductCategory.getSelectedItemPosition() == 0) { Toast.makeText(getContext(), "Select a category", Toast.LENGTH_SHORT).show(); return; } - if (!InputValidator.isNotEmpty(etProductPrice, "Price") || - !InputValidator.isPositiveDecimal(etProductPrice, "Price")) { + if (!InputValidator.isNotEmpty(binding.etProductPrice, "Price") || + !InputValidator.isPositiveDecimal(binding.etProductPrice, "Price")) { return; } - String name = etProductName.getText().toString().trim(); - String desc = etProductDesc.getText().toString().trim(); - BigDecimal price = new BigDecimal(etProductPrice.getText().toString().trim()); + String name = binding.etProductName.getText().toString().trim(); + String desc = binding.etProductDesc.getText().toString().trim(); + BigDecimal price = new BigDecimal(binding.etProductPrice.getText().toString().trim()); - CategoryDTO category = categoryList.get(spinnerCategory.getSelectedItemPosition() - 1); + CategoryDTO category = categoryList.get(binding.spinnerProductCategory.getSelectedItemPosition() - 1); ProductDTO dto = new ProductDTO(name, category.getCategoryId(), desc, price); if (isEditing) { diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/ProductSupplierDetailFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/ProductSupplierDetailFragment.java index bee0873d..4fd23fa0 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/ProductSupplierDetailFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/ProductSupplierDetailFragment.java @@ -8,7 +8,7 @@ import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.navigation.fragment.NavHostFragment; -import com.example.petstoremobile.R; +import com.example.petstoremobile.databinding.FragmentProductSupplierDetailBinding; import com.example.petstoremobile.dtos.*; import com.example.petstoremobile.utils.DialogUtils; import com.example.petstoremobile.utils.InputValidator; @@ -29,10 +29,7 @@ import dagger.hilt.android.AndroidEntryPoint; @AndroidEntryPoint public class ProductSupplierDetailFragment extends Fragment { - private TextView tvMode; - private Spinner spinnerProduct, spinnerSupplier; - private EditText etCost; - private Button btnSave, btnDelete, btnBack; + private FragmentProductSupplierDetailBinding binding; private boolean isEditing = false; private long editProductId = -1; @@ -58,28 +55,20 @@ public class ProductSupplierDetailFragment extends Fragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_product_supplier_detail, container, false); - initViews(view); + binding = FragmentProductSupplierDetailBinding.inflate(inflater, container, false); loadData(); handleArguments(); - btnBack.setOnClickListener(v -> navigateBack()); - btnSave.setOnClickListener(v -> save()); - btnDelete.setOnClickListener(v -> confirmDelete()); - return view; + binding.btnPSBack.setOnClickListener(v -> navigateBack()); + binding.btnSavePS.setOnClickListener(v -> save()); + binding.btnDeletePS.setOnClickListener(v -> confirmDelete()); + return binding.getRoot(); } - /** - * Initializes UI components from the layout. - */ - private void initViews(View v) { - tvMode = v.findViewById(R.id.tvPSMode); - spinnerProduct = v.findViewById(R.id.spinnerPSProduct); - spinnerSupplier = v.findViewById(R.id.spinnerPSSupplier); - etCost = v.findViewById(R.id.etPSCost); - btnSave = v.findViewById(R.id.btnSavePS); - btnDelete = v.findViewById(R.id.btnDeletePS); - btnBack = v.findViewById(R.id.btnPSBack); + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; } /** @@ -97,7 +86,7 @@ public class ProductSupplierDetailFragment extends Fragment { productViewModel.getAllProducts(null, 0, 200).observe(getViewLifecycleOwner(), resource -> { if (resource.status == Resource.Status.SUCCESS && resource.data != null) { productList = resource.data.getContent(); - SpinnerUtils.populateSpinner(requireContext(), spinnerProduct, productList, + SpinnerUtils.populateSpinner(requireContext(), binding.spinnerPSProduct, productList, ProductDTO::getProdName, "-- Select Product --", preselectedProductId, ProductDTO::getProdId); } @@ -111,7 +100,7 @@ public class ProductSupplierDetailFragment extends Fragment { supplierViewModel.getAllSuppliers(0, 200).observe(getViewLifecycleOwner(), resource -> { if (resource.status == Resource.Status.SUCCESS && resource.data != null) { supplierList = resource.data.getContent(); - SpinnerUtils.populateSpinner(requireContext(), spinnerSupplier, supplierList, + SpinnerUtils.populateSpinner(requireContext(), binding.spinnerPSSupplier, supplierList, SupplierDTO::getSupCompany, "-- Select Supplier --", preselectedSupplierId, SupplierDTO::getSupId); } @@ -129,12 +118,12 @@ public class ProductSupplierDetailFragment extends Fragment { editSupplierId = a.getLong("supplierId"); preselectedProductId = editProductId; preselectedSupplierId = editSupplierId; - etCost.setText(a.getString("cost")); - tvMode.setText("Edit Product Supplier"); - btnDelete.setVisibility(View.VISIBLE); + binding.etPSCost.setText(a.getString("cost")); + binding.tvPSMode.setText("Edit Product Supplier"); + binding.btnDeletePS.setVisibility(View.VISIBLE); } else { - tvMode.setText("Add Product Supplier"); - btnDelete.setVisibility(View.GONE); + binding.tvPSMode.setText("Add Product Supplier"); + binding.btnDeletePS.setVisibility(View.GONE); } } @@ -142,21 +131,21 @@ public class ProductSupplierDetailFragment extends Fragment { * Validates input and saves the product-supplier to the backend. */ private void save() { - if (spinnerProduct.getSelectedItemPosition() == 0) { + if (binding.spinnerPSProduct.getSelectedItemPosition() == 0) { Toast.makeText(getContext(), "Select a product", Toast.LENGTH_SHORT).show(); return; } - if (spinnerSupplier.getSelectedItemPosition() == 0) { + if (binding.spinnerPSSupplier.getSelectedItemPosition() == 0) { Toast.makeText(getContext(), "Select a supplier", Toast.LENGTH_SHORT).show(); return; } - if (!InputValidator.isNotEmpty(etCost, "Cost") || - !InputValidator.isPositiveDecimal(etCost, "Cost")) { + if (!InputValidator.isNotEmpty(binding.etPSCost, "Cost") || + !InputValidator.isPositiveDecimal(binding.etPSCost, "Cost")) { return; } - ProductDTO product = productList.get(spinnerProduct.getSelectedItemPosition() - 1); - SupplierDTO supplier = supplierList.get(spinnerSupplier.getSelectedItemPosition() - 1); - BigDecimal cost = new BigDecimal(etCost.getText().toString().trim()); + ProductDTO product = productList.get(binding.spinnerPSProduct.getSelectedItemPosition() - 1); + SupplierDTO supplier = supplierList.get(binding.spinnerPSSupplier.getSelectedItemPosition() - 1); + BigDecimal cost = new BigDecimal(binding.etPSCost.getText().toString().trim()); ProductSupplierDTO dto = new ProductSupplierDTO( product.getProdId(), supplier.getSupId(), cost); diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/PurchaseOrderDetailFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/PurchaseOrderDetailFragment.java index e4f05a26..5d9cf5a9 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/PurchaseOrderDetailFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/PurchaseOrderDetailFragment.java @@ -3,12 +3,11 @@ package com.example.petstoremobile.fragments.listfragments.detailfragments; import android.graphics.Color; import android.os.Bundle; import android.view.*; -import android.widget.*; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import androidx.navigation.fragment.NavHostFragment; -import com.example.petstoremobile.R; +import com.example.petstoremobile.databinding.FragmentPurchaseOrderDetailBinding; import dagger.hilt.android.AndroidEntryPoint; @@ -18,8 +17,7 @@ import dagger.hilt.android.AndroidEntryPoint; @AndroidEntryPoint public class PurchaseOrderDetailFragment extends Fragment { - private TextView tvId, tvSupplier, tvDate, tvStatus; - private Button btnBack; + private FragmentPurchaseOrderDetailBinding binding; /** * Inflates the layout, initializes views, and populates order data from arguments. @@ -27,38 +25,38 @@ public class PurchaseOrderDetailFragment extends Fragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_purchase_order_detail, container, false); - - tvId = view.findViewById(R.id.tvPODetailId); - tvSupplier = view.findViewById(R.id.tvPODetailSupplier); - tvDate = view.findViewById(R.id.tvPODetailDate); - tvStatus = view.findViewById(R.id.tvPODetailStatus); - btnBack = view.findViewById(R.id.btnPOBack); + binding = FragmentPurchaseOrderDetailBinding.inflate(inflater, container, false); Bundle a = getArguments(); if (a != null) { - tvId.setText("PO #" + a.getLong("purchaseOrderId")); - tvSupplier.setText(a.getString("supplierName")); - tvDate.setText(a.getString("orderDate")); + binding.tvPODetailId.setText("PO #" + a.getLong("purchaseOrderId")); + binding.tvPODetailSupplier.setText(a.getString("supplierName")); + binding.tvPODetailDate.setText(a.getString("orderDate")); String status = a.getString("status", ""); - tvStatus.setText(status); + binding.tvPODetailStatus.setText(status); switch (status) { case "Completed": - tvStatus.setTextColor(Color.parseColor("#4CAF50")); break; + binding.tvPODetailStatus.setTextColor(Color.parseColor("#4CAF50")); break; case "Pending": - tvStatus.setTextColor(Color.parseColor("#FF9800")); break; + binding.tvPODetailStatus.setTextColor(Color.parseColor("#FF9800")); break; case "Cancelled": - tvStatus.setTextColor(Color.parseColor("#F44336")); break; + binding.tvPODetailStatus.setTextColor(Color.parseColor("#F44336")); break; default: - tvStatus.setTextColor(Color.parseColor("#9E9E9E")); break; + binding.tvPODetailStatus.setTextColor(Color.parseColor("#9E9E9E")); break; } } - btnBack.setOnClickListener(v -> { + binding.btnPOBack.setOnClickListener(v -> { NavHostFragment.findNavController(this).popBackStack(); }); - return view; + return binding.getRoot(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; } } diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/RefundDetailFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/RefundDetailFragment.java index c607c6de..f4ce5f91 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/RefundDetailFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/RefundDetailFragment.java @@ -1,21 +1,17 @@ package com.example.petstoremobile.fragments.listfragments.detailfragments; import android.os.Bundle; +import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import androidx.navigation.fragment.NavHostFragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.EditText; -import android.widget.Spinner; -import android.widget.TextView; import android.widget.Toast; -import com.example.petstoremobile.R; -import com.example.petstoremobile.adapters.BlackTextArrayAdapter; + import com.example.petstoremobile.api.SaleApi; +import com.example.petstoremobile.databinding.FragmentRefundDetailBinding; import com.example.petstoremobile.fragments.listfragments.SaleFragment; import com.example.petstoremobile.utils.ActivityLogger; import com.example.petstoremobile.utils.InputValidator; @@ -28,10 +24,7 @@ import dagger.hilt.android.AndroidEntryPoint; @AndroidEntryPoint public class RefundDetailFragment extends Fragment { - private EditText etRefundSaleId, etRefundReason; - private TextView tvSaleInfo; - private Spinner spinnerRefundPayment; - private Button btnLoadSale, btnProcessRefund, btnBack; + private FragmentRefundDetailBinding binding; private int saleId; private SaleFragment saleFragment; @@ -42,23 +35,28 @@ public class RefundDetailFragment extends Fragment { } @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_refund_detail, container, false); + binding = FragmentRefundDetailBinding.inflate(inflater, container, false); - initViews(view); setupSpinner(); handleArguments(); - btnBack.setOnClickListener(v -> goBack()); - btnLoadSale.setOnClickListener(v -> loadSaleDetails()); - btnProcessRefund.setOnClickListener(v -> processRefund()); + binding.btnRefundBack.setOnClickListener(v -> goBack()); + binding.btnLoadSale.setOnClickListener(v -> loadSaleDetails()); + binding.btnProcessRefund.setOnClickListener(v -> processRefund()); - return view; + return binding.getRoot(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; } private void loadSaleDetails() { - String idText = etRefundSaleId.getText().toString().trim(); + String idText = binding.etRefundSaleId.getText().toString().trim(); if (idText.isEmpty()) { Toast.makeText(getContext(), "Enter a Sale ID", Toast.LENGTH_SHORT).show(); return; @@ -68,22 +66,21 @@ public class RefundDetailFragment extends Fragment { int id = Integer.parseInt(idText); // TODO: Replace with actual API call - GET v1/sales/{id} // For now show placeholder info - tvSaleInfo.setText("Sale ID: " + id + " loaded. Enter reason and payment method to process refund."); - tvSaleInfo.setTextColor(getResources().getColor(android.R.color.holo_green_dark)); + binding.tvSaleInfo.setText("Sale ID: " + id + " loaded. Enter reason and payment method to process refund."); + binding.tvSaleInfo.setTextColor(getResources().getColor(android.R.color.holo_green_dark)); } catch (NumberFormatException e) { Toast.makeText(getContext(), "Invalid Sale ID", Toast.LENGTH_SHORT).show(); } } private void processRefund() { - if (!InputValidator.isNotEmpty(etRefundSaleId, "Sale ID")) + if (!InputValidator.isNotEmpty(binding.etRefundSaleId, "Sale ID")) return; - if (!InputValidator.isNotEmpty(etRefundReason, "Refund Reason")) + if (!InputValidator.isNotEmpty(binding.etRefundReason, "Refund Reason")) return; - String idText = etRefundSaleId.getText().toString().trim(); - String reason = etRefundReason.getText().toString().trim(); - String payment = spinnerRefundPayment.getSelectedItem().toString(); + String idText = binding.etRefundSaleId.getText().toString().trim(); + String reason = binding.etRefundReason.getText().toString().trim(); try { int id = Integer.parseInt(idText); @@ -101,16 +98,16 @@ public class RefundDetailFragment extends Fragment { private void handleArguments() { if (getArguments() != null && getArguments().containsKey("saleId")) { saleId = getArguments().getInt("saleId"); - etRefundSaleId.setText(String.valueOf(saleId)); + binding.etRefundSaleId.setText(String.valueOf(saleId)); String info = "Sale Date: " + getArguments().getString("saleDate") + " | Employee: " + getArguments().getString("employeeName") + " | Total: $" + String.format("%.2f", getArguments().getDouble("total")) + " | Payment: " + getArguments().getString("paymentMethod"); - tvSaleInfo.setText(info); - tvSaleInfo.setTextColor(getResources().getColor(android.R.color.holo_green_dark)); + binding.tvSaleInfo.setText(info); + binding.tvSaleInfo.setTextColor(getResources().getColor(android.R.color.holo_green_dark)); // Pre-select payment method - SpinnerUtils.setSelectionByValue(spinnerRefundPayment, getArguments().getString("paymentMethod")); + SpinnerUtils.setSelectionByValue(binding.spinnerRefundPayment, getArguments().getString("paymentMethod")); } } @@ -118,18 +115,8 @@ public class RefundDetailFragment extends Fragment { NavHostFragment.findNavController(this).popBackStack(); } - private void initViews(View view) { - etRefundSaleId = view.findViewById(R.id.etRefundSaleId); - etRefundReason = view.findViewById(R.id.etRefundReason); - tvSaleInfo = view.findViewById(R.id.tvSaleInfo); - spinnerRefundPayment = view.findViewById(R.id.spinnerRefundPayment); - btnLoadSale = view.findViewById(R.id.btnLoadSale); - btnProcessRefund = view.findViewById(R.id.btnProcessRefund); - btnBack = view.findViewById(R.id.btnRefundBack); - } - private void setupSpinner() { - SpinnerUtils.setupStringSpinner(requireContext(), spinnerRefundPayment, + SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerRefundPayment, new String[] { "Cash", "Card", "Debit" }); } } diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/ServiceDetailFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/ServiceDetailFragment.java index 5cb9a7c2..7f9c053b 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/ServiceDetailFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/ServiceDetailFragment.java @@ -10,12 +10,10 @@ import androidx.navigation.fragment.NavHostFragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.Button; -import android.widget.EditText; -import android.widget.TextView; import android.widget.Toast; 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.DialogUtils; @@ -31,9 +29,7 @@ import dagger.hilt.android.AndroidEntryPoint; @AndroidEntryPoint public class ServiceDetailFragment extends Fragment { - private TextView tvMode, tvServiceId; - private EditText etServiceName, etServiceDesc, etServiceDuration, etServicePrice; - private Button btnSaveService, btnDeleteService, btnBack; + private FragmentServiceDetailBinding binding; private int serviceId; private boolean isEditing = false; @@ -48,18 +44,23 @@ public class ServiceDetailFragment extends Fragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_service_detail, container, false); + binding = FragmentServiceDetailBinding.inflate(inflater, container, false); //get controls from layout and display the view depending on the mode - initViews(view); handleArguments(); //set button click listeners - btnBack.setOnClickListener(v -> navigateBack()); - btnSaveService.setOnClickListener(v -> saveService()); - btnDeleteService.setOnClickListener(v -> deleteService()); + binding.btnBack.setOnClickListener(v -> navigateBack()); + binding.btnSaveService.setOnClickListener(v -> saveService()); + binding.btnDeleteService.setOnClickListener(v -> deleteService()); - return view; + return binding.getRoot(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; } /** @@ -67,16 +68,16 @@ public class ServiceDetailFragment extends Fragment { */ private void saveService() { // Validates all fields using InputValidator - if (!InputValidator.isNotEmpty(etServiceName, "Service Name")) return; - if (!InputValidator.isNotEmpty(etServiceDesc, "Description")) return; - if (!InputValidator.isPositiveInteger(etServiceDuration, "Duration")) return; - if (!InputValidator.isPositiveDecimal(etServicePrice, "Price")) return; + if (!InputValidator.isNotEmpty(binding.etServiceName, "Service Name")) return; + if (!InputValidator.isNotEmpty(binding.etServiceDesc, "Description")) return; + if (!InputValidator.isPositiveInteger(binding.etServiceDuration, "Duration")) return; + if (!InputValidator.isPositiveDecimal(binding.etServicePrice, "Price")) return; //get all the values from the fields - String name = etServiceName.getText().toString().trim(); - String desc = etServiceDesc.getText().toString().trim(); - int duration = Integer.parseInt(etServiceDuration.getText().toString().trim()); - double price = Double.parseDouble(etServicePrice.getText().toString().trim()); + String name = binding.etServiceName.getText().toString().trim(); + String desc = binding.etServiceDesc.getText().toString().trim(); + int duration = Integer.parseInt(binding.etServiceDuration.getText().toString().trim()); + double price = Double.parseDouble(binding.etServicePrice.getText().toString().trim()); //create a service object to send to the API ServiceDTO serviceDTO = new ServiceDTO(); @@ -143,36 +144,21 @@ public class ServiceDetailFragment extends Fragment { // Get service data from arguments and populate fields isEditing = true; serviceId = getArguments().getInt("serviceId"); - tvMode.setText("Edit Service"); - tvServiceId.setText("ID: " + serviceId); - etServiceName.setText(getArguments().getString("serviceName")); - etServiceDesc.setText(getArguments().getString("serviceDesc")); - etServiceDuration.setText(String.valueOf(getArguments().getInt("serviceDuration"))); - etServicePrice.setText(String.valueOf(getArguments().getDouble("servicePrice"))); - btnDeleteService.setVisibility(View.VISIBLE); + binding.tvMode.setText("Edit Service"); + binding.tvServiceId.setText("ID: " + serviceId); + binding.etServiceName.setText(getArguments().getString("serviceName")); + binding.etServiceDesc.setText(getArguments().getString("serviceDesc")); + binding.etServiceDuration.setText(String.valueOf(getArguments().getInt("serviceDuration"))); + binding.etServicePrice.setText(String.valueOf(getArguments().getDouble("servicePrice"))); + binding.btnDeleteService.setVisibility(View.VISIBLE); } else { // Service is being added // Set default values for add a new service isEditing = false; - tvMode.setText("Add Service"); - tvServiceId.setVisibility(View.GONE); - btnDeleteService.setVisibility(View.GONE); - btnSaveService.setText("Add"); + binding.tvMode.setText("Add Service"); + binding.tvServiceId.setVisibility(View.GONE); + binding.btnDeleteService.setVisibility(View.GONE); + binding.btnSaveService.setText("Add"); } } - - /** - * Initializes UI components from the layout. - */ - private void initViews(View view) { - tvMode = view.findViewById(R.id.tvMode); - tvServiceId = view.findViewById(R.id.tvServiceId); - etServiceName = view.findViewById(R.id.etServiceName); - etServiceDesc = view.findViewById(R.id.etServiceDesc); - etServiceDuration = view.findViewById(R.id.etServiceDuration); - etServicePrice = view.findViewById(R.id.etServicePrice); - btnSaveService = view.findViewById(R.id.btnSaveService); - btnDeleteService = view.findViewById(R.id.btnDeleteService); - btnBack = view.findViewById(R.id.btnBack); - } } diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/SupplierDetailFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/SupplierDetailFragment.java index 4477712f..5d52606d 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/SupplierDetailFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/SupplierDetailFragment.java @@ -10,12 +10,9 @@ import androidx.navigation.fragment.NavHostFragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.Button; -import android.widget.EditText; -import android.widget.TextView; import android.widget.Toast; -import com.example.petstoremobile.R; +import com.example.petstoremobile.databinding.FragmentSupplierDetailBinding; import com.example.petstoremobile.dtos.SupplierDTO; import com.example.petstoremobile.utils.ActivityLogger; import com.example.petstoremobile.utils.DialogUtils; @@ -32,9 +29,7 @@ import dagger.hilt.android.AndroidEntryPoint; @AndroidEntryPoint public class SupplierDetailFragment extends Fragment { - private TextView tvMode, tvSupId; - private EditText etSupCompany, etSupContactFirstName, etSupContactLastName, etSupEmail, etSupPhone; - private Button btnSaveSupplier, btnDeleteSupplier, btnBack; + private FragmentSupplierDetailBinding binding; private int supId; private boolean isEditing = false; @@ -49,18 +44,25 @@ public class SupplierDetailFragment extends Fragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_supplier_detail, container, false); + binding = FragmentSupplierDetailBinding.inflate(inflater, container, false); + + // Add phone number formatting (CA) and limit length to 14 characters + UIUtils.formatPhoneInput(binding.etSupPhone); - //get controls from layout and display the view depending on the mode - initViews(view); handleArguments(); //set button click listeners - btnBack.setOnClickListener(v -> navigateBack()); - btnSaveSupplier.setOnClickListener(v -> saveSupplier()); - btnDeleteSupplier.setOnClickListener(v -> deleteSupplier()); + binding.btnBack.setOnClickListener(v -> navigateBack()); + binding.btnSaveSupplier.setOnClickListener(v -> saveSupplier()); + binding.btnDeleteSupplier.setOnClickListener(v -> deleteSupplier()); - return view; + return binding.getRoot(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; } /** @@ -68,18 +70,18 @@ public class SupplierDetailFragment extends Fragment { */ private void saveSupplier() { // Validates all fields using InputValidator - if (!InputValidator.isNotEmpty(etSupCompany, "Company Name")) return; - if (!InputValidator.isNotEmpty(etSupContactFirstName, "First Name")) return; - if (!InputValidator.isNotEmpty(etSupContactLastName, "Last Name")) return; - if (!InputValidator.isValidEmail(etSupEmail)) return; - if (!InputValidator.isValidPhone(etSupPhone)) return; + if (!InputValidator.isNotEmpty(binding.etSupCompany, "Company Name")) return; + if (!InputValidator.isNotEmpty(binding.etSupContactFirstName, "First Name")) return; + if (!InputValidator.isNotEmpty(binding.etSupContactLastName, "Last Name")) return; + if (!InputValidator.isValidEmail(binding.etSupEmail)) return; + if (!InputValidator.isValidPhone(binding.etSupPhone)) return; //get all the values from the fields - String company = etSupCompany.getText().toString().trim(); - String firstName = etSupContactFirstName.getText().toString().trim(); - String lastName = etSupContactLastName.getText().toString().trim(); - String email = etSupEmail.getText().toString().trim(); - String phone = etSupPhone.getText().toString().trim(); + String company = binding.etSupCompany.getText().toString().trim(); + String firstName = binding.etSupContactFirstName.getText().toString().trim(); + String lastName = binding.etSupContactLastName.getText().toString().trim(); + String email = binding.etSupEmail.getText().toString().trim(); + String phone = binding.etSupPhone.getText().toString().trim(); //create a supplier object to send to the API SupplierDTO supplierDTO = new SupplierDTO(); @@ -148,42 +150,22 @@ public class SupplierDetailFragment extends Fragment { // Get supplier data from arguments and populate fields isEditing = true; supId = getArguments().getInt("supId"); - tvMode.setText("Edit Supplier"); - tvSupId.setText("ID: " + supId); - etSupCompany.setText(getArguments().getString("supCompany")); - etSupContactFirstName.setText(getArguments().getString("supContactFirstName")); - etSupContactLastName.setText(getArguments().getString("supContactLastName")); - etSupEmail.setText(getArguments().getString("supEmail")); - etSupPhone.setText(getArguments().getString("supPhone")); - btnDeleteSupplier.setVisibility(View.VISIBLE); + binding.tvMode.setText("Edit Supplier"); + binding.tvSupId.setText("ID: " + supId); + binding.etSupCompany.setText(getArguments().getString("supCompany")); + binding.etSupContactFirstName.setText(getArguments().getString("supContactFirstName")); + binding.etSupContactLastName.setText(getArguments().getString("supContactLastName")); + binding.etSupEmail.setText(getArguments().getString("supEmail")); + binding.etSupPhone.setText(getArguments().getString("supPhone")); + binding.btnDeleteSupplier.setVisibility(View.VISIBLE); } else { // Supplier is being added // Set default values for add a new supplier isEditing = false; - tvMode.setText("Add Supplier"); - tvSupId.setVisibility(View.GONE); - btnDeleteSupplier.setVisibility(View.GONE); - btnSaveSupplier.setText("Add"); + binding.tvMode.setText("Add Supplier"); + binding.tvSupId.setVisibility(View.GONE); + binding.btnDeleteSupplier.setVisibility(View.GONE); + binding.btnSaveSupplier.setText("Add"); } } - - /** - * Initializes the UI components and sets up formatting for phone input. - */ - private void initViews(View view) { - tvMode = view.findViewById(R.id.tvMode); - tvSupId = view.findViewById(R.id.tvSupId); - etSupCompany = view.findViewById(R.id.etSupCompany); - etSupContactFirstName = view.findViewById(R.id.etSupContactFirstName); - etSupContactLastName = view.findViewById(R.id.etSupContactLastName); - etSupEmail = view.findViewById(R.id.etSupEmail); - etSupPhone = view.findViewById(R.id.etSupPhone); - - // Add phone number formatting (CA) and limit length to 14 characters - UIUtils.formatPhoneInput(etSupPhone); - - btnSaveSupplier = view.findViewById(R.id.btnSaveSupplier); - btnDeleteSupplier = view.findViewById(R.id.btnDeleteSupplier); - btnBack = view.findViewById(R.id.btnBack); - } }