changed detailed fragment to fill data from the backend
This commit is contained in:
@@ -58,41 +58,45 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Core AndroidX & UI
|
||||
implementation(libs.appcompat)
|
||||
implementation(libs.material)
|
||||
implementation(libs.activity)
|
||||
implementation(libs.constraintlayout)
|
||||
implementation(libs.swiperefreshlayout)
|
||||
implementation(libs.viewpager2)
|
||||
|
||||
// Hilt Dependency Injection
|
||||
implementation(libs.hilt.android)
|
||||
annotationProcessor(libs.hilt.compiler)
|
||||
|
||||
// Navigation Component
|
||||
implementation(libs.navigation.fragment)
|
||||
implementation(libs.navigation.ui)
|
||||
|
||||
implementation("com.squareup.retrofit2:retrofit:2.9.0")
|
||||
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
|
||||
implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
|
||||
implementation("com.squareup.okhttp3:okhttp:4.12.0")
|
||||
// Networking
|
||||
implementation(libs.retrofit)
|
||||
implementation(libs.retrofit.gson)
|
||||
implementation(libs.okhttp)
|
||||
implementation(libs.okhttp.logging)
|
||||
|
||||
implementation("com.google.android.material:material:1.11.0")
|
||||
implementation("androidx.viewpager2:viewpager2:1.1.0")
|
||||
// CameraX
|
||||
implementation(libs.camera.core)
|
||||
implementation(libs.camera.camera2)
|
||||
implementation(libs.camera.lifecycle)
|
||||
implementation(libs.camera.view)
|
||||
|
||||
implementation("androidx.camera:camera-core:1.4.0")
|
||||
implementation("androidx.camera:camera-camera2:1.4.0")
|
||||
implementation("androidx.camera:camera-lifecycle:1.4.0")
|
||||
implementation("androidx.camera:camera-view:1.4.0")
|
||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||
implementation(libs.swiperefreshlayout)
|
||||
// Image Loading
|
||||
implementation(libs.glide)
|
||||
annotationProcessor(libs.glide.compiler)
|
||||
|
||||
// Other Third-party Libraries
|
||||
implementation("com.github.NaikSoftware:StompProtocolAndroid:1.6.6")
|
||||
implementation("io.reactivex.rxjava2:rxjava:2.2.21")
|
||||
implementation("io.reactivex.rxjava2:rxandroid:2.1.1")
|
||||
|
||||
implementation("com.github.bumptech.glide:glide:4.16.0")
|
||||
annotationProcessor("com.github.bumptech.glide:compiler:4.16.0")
|
||||
|
||||
implementation("com.github.prolificinteractive:material-calendarview:2.0.1")
|
||||
|
||||
// Testing
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.ext.junit)
|
||||
androidTestImplementation(libs.espresso.core)
|
||||
|
||||
@@ -3,9 +3,14 @@ package com.example.petstoremobile.adapters;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.bumptech.glide.load.model.GlideUrl;
|
||||
import com.bumptech.glide.load.model.LazyHeaders;
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.models.Message;
|
||||
import java.util.List;
|
||||
@@ -17,6 +22,7 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
||||
|
||||
private final List<Message> messages;
|
||||
private Long currentUserId;
|
||||
private String token;
|
||||
|
||||
public MessageAdapter(List<Message> messages, Long currentUserId) {
|
||||
this.messages = messages;
|
||||
@@ -28,6 +34,10 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
Message m = messages.get(position);
|
||||
@@ -52,27 +62,70 @@ public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||
Message m = messages.get(position);
|
||||
if (holder instanceof SentHolder) ((SentHolder) holder).bind(m);
|
||||
if (holder instanceof ReceivedHolder) ((ReceivedHolder) holder).bind(m);
|
||||
if (holder instanceof SentHolder) ((SentHolder) holder).bind(m, token);
|
||||
if (holder instanceof ReceivedHolder) ((ReceivedHolder) holder).bind(m, token);
|
||||
}
|
||||
|
||||
@Override public int getItemCount() { return messages.size(); }
|
||||
|
||||
static class SentHolder extends RecyclerView.ViewHolder {
|
||||
TextView tvMessage;
|
||||
TextView tvMessage, tvAttachmentName;
|
||||
ImageView ivAttachment;
|
||||
SentHolder(View v) {
|
||||
super(v);
|
||||
tvMessage = v.findViewById(R.id.tvMessageContent); // updated
|
||||
tvMessage = v.findViewById(R.id.tvMessageContent);
|
||||
tvAttachmentName = v.findViewById(R.id.tvAttachmentName);
|
||||
ivAttachment = v.findViewById(R.id.ivAttachment);
|
||||
}
|
||||
void bind(Message m, String token) {
|
||||
tvMessage.setText(m.getContent());
|
||||
displayAttachment(m, ivAttachment, tvAttachmentName, token);
|
||||
}
|
||||
void bind(Message m) { tvMessage.setText(m.getContent()); }
|
||||
}
|
||||
|
||||
static class ReceivedHolder extends RecyclerView.ViewHolder {
|
||||
TextView tvMessage;
|
||||
TextView tvMessage, tvAttachmentName;
|
||||
ImageView ivAttachment;
|
||||
ReceivedHolder(View v) {
|
||||
super(v);
|
||||
tvMessage = v.findViewById(R.id.tvMessageContent); // updated
|
||||
tvMessage = v.findViewById(R.id.tvMessageContent);
|
||||
tvAttachmentName = v.findViewById(R.id.tvAttachmentName);
|
||||
ivAttachment = v.findViewById(R.id.ivAttachment);
|
||||
}
|
||||
void bind(Message m, String token) {
|
||||
tvMessage.setText(m.getContent());
|
||||
displayAttachment(m, ivAttachment, tvAttachmentName, token);
|
||||
}
|
||||
}
|
||||
|
||||
// helper function to display the attachment to the chat bubble
|
||||
private static void displayAttachment(Message m, ImageView iv, TextView tvName, String token) {
|
||||
if (m.getAttachmentUrl() != null) {
|
||||
if (m.getAttachmentType() != null && m.getAttachmentType().startsWith("image/")) {
|
||||
iv.setVisibility(View.VISIBLE);
|
||||
tvName.setVisibility(View.GONE);
|
||||
|
||||
Object loadTarget = m.getAttachmentUrl();
|
||||
if (token != null && m.getAttachmentUrl().startsWith("http")) {
|
||||
loadTarget = new GlideUrl(m.getAttachmentUrl(), new LazyHeaders.Builder()
|
||||
.addHeader("Authorization", "Bearer " + token)
|
||||
.build());
|
||||
}
|
||||
|
||||
Glide.with(iv.getContext())
|
||||
.load(loadTarget)
|
||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||
.placeholder(R.drawable.placeholder)
|
||||
.error(R.drawable.placeholder)
|
||||
.into(iv);
|
||||
} else {
|
||||
iv.setVisibility(View.GONE);
|
||||
tvName.setVisibility(View.VISIBLE);
|
||||
tvName.setText(m.getAttachmentName() != null ? m.getAttachmentName() : "Attachment");
|
||||
}
|
||||
} else {
|
||||
iv.setVisibility(View.GONE);
|
||||
tvName.setVisibility(View.GONE);
|
||||
}
|
||||
void bind(Message m) { tvMessage.setText(m.getContent()); }
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,14 @@ package com.example.petstoremobile.api;
|
||||
import com.example.petstoremobile.dtos.MessageDTO;
|
||||
import com.example.petstoremobile.dtos.SendMessageRequest;
|
||||
import java.util.List;
|
||||
import okhttp3.MultipartBody;
|
||||
import okhttp3.RequestBody;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.Body;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Multipart;
|
||||
import retrofit2.http.POST;
|
||||
import retrofit2.http.Part;
|
||||
import retrofit2.http.Path;
|
||||
|
||||
//api calls to get and send messages
|
||||
@@ -17,4 +21,12 @@ public interface MessageApi {
|
||||
|
||||
@POST("api/v1/chat/conversations/{id}/messages")
|
||||
Call<MessageDTO> sendMessage(@Path("id") Long conversationId, @Body SendMessageRequest request);
|
||||
|
||||
@Multipart
|
||||
@POST("api/v1/chat/conversations/{id}/messages/attachment")
|
||||
Call<MessageDTO> sendMessageWithAttachment(
|
||||
@Path("id") Long conversationId,
|
||||
@Part("content") RequestBody content,
|
||||
@Part MultipartBody.Part file
|
||||
);
|
||||
}
|
||||
@@ -12,6 +12,11 @@ public interface ProductSupplierApi {
|
||||
@Query("page") int page,
|
||||
@Query("size") int size);
|
||||
|
||||
@GET("api/v1/product-suppliers/{productId}/{supplierId}")
|
||||
Call<ProductSupplierDTO> getProductSupplierById(
|
||||
@Path("productId") Long productId,
|
||||
@Path("supplierId") Long supplierId);
|
||||
|
||||
@POST("api/v1/product-suppliers")
|
||||
Call<ProductSupplierDTO> createProductSupplier(@Body ProductSupplierDTO dto);
|
||||
|
||||
|
||||
@@ -14,33 +14,35 @@ import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.view.GravityCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.adapters.ChatAdapter;
|
||||
import com.example.petstoremobile.adapters.MessageAdapter;
|
||||
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.SendMessageRequest;
|
||||
import com.example.petstoremobile.models.Chat;
|
||||
import com.example.petstoremobile.models.Message;
|
||||
import com.example.petstoremobile.services.ChatNotificationService;
|
||||
import com.example.petstoremobile.utils.RetrofitUtils;
|
||||
import com.example.petstoremobile.utils.FileUtils;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.viewmodels.ChatViewModel;
|
||||
import com.example.petstoremobile.websocket.StompChatManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.MultipartBody;
|
||||
import okhttp3.RequestBody;
|
||||
|
||||
@AndroidEntryPoint
|
||||
public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickListener, StompChatManager.MessageListener,
|
||||
@@ -49,6 +51,7 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
private static final String TAG = "ChatFragment";
|
||||
|
||||
private FragmentChatBinding binding;
|
||||
private ChatViewModel viewModel;
|
||||
|
||||
// Adapters
|
||||
private ChatAdapter chatAdapter;
|
||||
@@ -60,10 +63,6 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
private final Map<Long, String> customerNames = new HashMap<>();
|
||||
private Uri pendingAttachmentUri;
|
||||
|
||||
// APIs
|
||||
@Inject ChatApi chatApi;
|
||||
@Inject CustomerApi customerApi;
|
||||
@Inject MessageApi messageApi;
|
||||
@Inject TokenManager tokenManager;
|
||||
@Inject @Named("baseUrl") String baseUrl;
|
||||
|
||||
@@ -79,6 +78,7 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(ChatViewModel.class);
|
||||
attachmentLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {
|
||||
@@ -183,54 +183,53 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a list of customers from the API to display customer names for the chat list.
|
||||
* Fetches a list of customers from the ViewModel to display customer names for the chat list.
|
||||
*/
|
||||
private void loadCustomers() {
|
||||
customerApi.getAllCustomers(0, 100).enqueue(RetrofitUtils.createSilentCallback(TAG, result -> {
|
||||
for (CustomerDTO c : result.getContent()) {
|
||||
customerNames.put(c.getCustomerId(), c.getFullName());
|
||||
viewModel.getAllCustomers(0, 100).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
resource.data.getContent().forEach(c -> customerNames.put(c.getCustomerId(), c.getFullName()));
|
||||
loadConversations();
|
||||
}
|
||||
loadConversations();
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all conversations for the current user and populates the chat drawer.
|
||||
* Retrieves all conversations for the current user through the ViewModel and populates the chat drawer.
|
||||
*/
|
||||
private void loadConversations() {
|
||||
chatApi.getAllConversations().enqueue(RetrofitUtils.createSilentCallback(TAG, result -> {
|
||||
chatList.clear();
|
||||
List<Chat> loaded = result.stream()
|
||||
.map(dto -> {
|
||||
String name = customerNames.getOrDefault(
|
||||
dto.getCustomerId(), "Customer #" + dto.getCustomerId());
|
||||
return new Chat(String.valueOf(dto.getId()),
|
||||
name, dto.getLastMessage(),
|
||||
dto.getCustomerId(), dto.getStaffId());
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
chatList.addAll(loaded);
|
||||
chatAdapter.notifyDataSetChanged();
|
||||
|
||||
if (activeConversationId != null) {
|
||||
setConversationActive(true);
|
||||
// Update title to customer name of active conversation
|
||||
for (Chat chat : chatList) {
|
||||
if (chat.getChatId().equals(String.valueOf(activeConversationId))) {
|
||||
binding.tvChatTitle.setText(chat.getCustomerName());
|
||||
break;
|
||||
viewModel.getAllConversations().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
chatList.clear();
|
||||
for (ConversationDTO dto : resource.data) {
|
||||
String name = customerNames.getOrDefault(
|
||||
dto.getCustomerId(), "Customer #" + dto.getCustomerId());
|
||||
chatList.add(new Chat(String.valueOf(dto.getId()),
|
||||
name, dto.getLastMessage(),
|
||||
dto.getCustomerId(), dto.getStaffId()));
|
||||
}
|
||||
chatAdapter.notifyDataSetChanged();
|
||||
|
||||
if (activeConversationId != null) {
|
||||
setConversationActive(true);
|
||||
// Update title to customer name of active conversation
|
||||
for (Chat chat : chatList) {
|
||||
if (chat.getChatId().equals(String.valueOf(activeConversationId))) {
|
||||
binding.tvChatTitle.setText(chat.getCustomerName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (stompChatManager != null) {
|
||||
stompChatManager.subscribeToConversation(activeConversationId);
|
||||
}
|
||||
loadMessageHistory(activeConversationId);
|
||||
} else {
|
||||
messageList.clear();
|
||||
messageAdapter.notifyDataSetChanged();
|
||||
setConversationActive(false);
|
||||
}
|
||||
if (stompChatManager != null) {
|
||||
stompChatManager.subscribeToConversation(activeConversationId);
|
||||
}
|
||||
loadMessageHistory(activeConversationId);
|
||||
} else {
|
||||
messageList.clear();
|
||||
messageAdapter.notifyDataSetChanged();
|
||||
setConversationActive(false);
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -251,21 +250,23 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the full message history for a specific conversation from the API.
|
||||
* Fetches the full message history for a specific conversation from the ViewModel.
|
||||
*/
|
||||
private void loadMessageHistory(Long conversationId) {
|
||||
messageApi.getMessages(conversationId).enqueue(RetrofitUtils.createSilentCallback(TAG, result -> {
|
||||
messageList.clear();
|
||||
for (MessageDTO dto : result) {
|
||||
messageList.add(dtoToModel(dto));
|
||||
viewModel.getMessages(conversationId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
messageList.clear();
|
||||
for (MessageDTO dto : resource.data) {
|
||||
messageList.add(dtoToModel(dto));
|
||||
}
|
||||
messageAdapter.notifyDataSetChanged();
|
||||
scrollToBottom();
|
||||
}
|
||||
messageAdapter.notifyDataSetChanged();
|
||||
scrollToBottom();
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a plain text message to the currently active conversation.
|
||||
* Sends a plain text message to the currently active conversation through the ViewModel.
|
||||
*/
|
||||
private void sendMessage() {
|
||||
//check if a chat is selected
|
||||
@@ -278,14 +279,15 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
//clear text field after sending
|
||||
binding.etMessage.setText("");
|
||||
|
||||
//calls api to send the message
|
||||
messageApi.sendMessage(activeConversationId, new SendMessageRequest(text))
|
||||
.enqueue(RetrofitUtils.createSilentCallback(TAG, result -> {
|
||||
messageList.add(dtoToModel(result));
|
||||
messageAdapter.notifyItemInserted(messageList.size() - 1);
|
||||
scrollToBottom();
|
||||
loadConversations();
|
||||
}));
|
||||
//calls viewmodel to send the message
|
||||
viewModel.sendMessage(activeConversationId, new SendMessageRequest(text)).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
messageList.add(dtoToModel(resource.data));
|
||||
messageAdapter.notifyItemInserted(messageList.size() - 1);
|
||||
scrollToBottom();
|
||||
loadConversations();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -351,13 +353,34 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles sending a message that includes a file attachment.
|
||||
* Handles sending a message that includes a file attachment via the ViewModel.
|
||||
*/
|
||||
private void sendWithAttachment(Uri uri) {
|
||||
if (activeConversationId == null) return;
|
||||
String text = binding.etMessage.getText().toString().trim();
|
||||
binding.etMessage.setText("");
|
||||
removeAttachment();
|
||||
|
||||
//TODO: send the message with attachment when backend is done
|
||||
Log.d(TAG, "Send with attachment happening");
|
||||
try {
|
||||
File file = FileUtils.getFileFromUri(requireContext(), uri);
|
||||
if (file == null) return;
|
||||
|
||||
String mimeType = requireContext().getContentResolver().getType(uri);
|
||||
RequestBody requestFile = RequestBody.create(file, MediaType.parse(mimeType != null ? mimeType : "application/octet-stream"));
|
||||
MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", file.getName(), requestFile);
|
||||
RequestBody contentPart = RequestBody.create(text, MediaType.parse("text/plain"));
|
||||
|
||||
viewModel.sendMessageWithAttachment(activeConversationId, contentPart, filePart).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
messageList.add(dtoToModel(resource.data));
|
||||
messageAdapter.notifyItemInserted(messageList.size() - 1);
|
||||
scrollToBottom();
|
||||
loadConversations();
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error sending message with attachment", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -376,8 +399,10 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
|
||||
//else add the message to the active chat if it's not from the current user
|
||||
messageList.add(dtoToModel(dto));
|
||||
messageAdapter.notifyItemInserted(messageList.size() - 1);
|
||||
scrollToBottom();
|
||||
requireActivity().runOnUiThread(() -> {
|
||||
messageAdapter.notifyItemInserted(messageList.size() - 1);
|
||||
scrollToBottom();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -385,41 +410,43 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
*/
|
||||
@Override
|
||||
public void onConversationUpdated(ConversationDTO dto) {
|
||||
boolean updated = false;
|
||||
String name = customerNames.getOrDefault(
|
||||
dto.getCustomerId(), "Customer #" + dto.getCustomerId());
|
||||
requireActivity().runOnUiThread(() -> {
|
||||
boolean updated = false;
|
||||
String name = customerNames.getOrDefault(
|
||||
dto.getCustomerId(), "Customer #" + dto.getCustomerId());
|
||||
|
||||
for (int i = 0; i < chatList.size(); i++) {
|
||||
Chat existing = chatList.get(i);
|
||||
if (existing.getChatId().equals(String.valueOf(dto.getId()))) {
|
||||
chatList.set(i, new Chat(
|
||||
for (int i = 0; i < chatList.size(); i++) {
|
||||
Chat existing = chatList.get(i);
|
||||
if (existing.getChatId().equals(String.valueOf(dto.getId()))) {
|
||||
chatList.set(i, new Chat(
|
||||
String.valueOf(dto.getId()),
|
||||
name,
|
||||
dto.getLastMessage(),
|
||||
dto.getCustomerId(),
|
||||
dto.getStaffId()
|
||||
));
|
||||
chatAdapter.notifyItemChanged(i);
|
||||
updated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!updated) {
|
||||
chatList.add(0, new Chat(
|
||||
String.valueOf(dto.getId()),
|
||||
name,
|
||||
dto.getLastMessage(),
|
||||
dto.getCustomerId(),
|
||||
dto.getStaffId()
|
||||
));
|
||||
chatAdapter.notifyItemChanged(i);
|
||||
updated = true;
|
||||
break;
|
||||
chatAdapter.notifyItemInserted(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!updated) {
|
||||
chatList.add(0, new Chat(
|
||||
String.valueOf(dto.getId()),
|
||||
name,
|
||||
dto.getLastMessage(),
|
||||
dto.getCustomerId(),
|
||||
dto.getStaffId()
|
||||
));
|
||||
chatAdapter.notifyItemInserted(0);
|
||||
}
|
||||
|
||||
if (activeConversationId != null && activeConversationId.equals(dto.getId())) {
|
||||
setConversationActive(true);
|
||||
binding.tvChatTitle.setText(name);
|
||||
}
|
||||
if (activeConversationId != null && activeConversationId.equals(dto.getId())) {
|
||||
setConversationActive(true);
|
||||
binding.tvChatTitle.setText(name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -430,10 +457,12 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
if (!isAdded()) {
|
||||
return;
|
||||
}
|
||||
loadConversations();
|
||||
if (activeConversationId != null) {
|
||||
loadMessageHistory(activeConversationId);
|
||||
}
|
||||
requireActivity().runOnUiThread(() -> {
|
||||
loadConversations();
|
||||
if (activeConversationId != null) {
|
||||
loadMessageHistory(activeConversationId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -444,7 +473,7 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
if (!isAdded()) {
|
||||
return;
|
||||
}
|
||||
loadConversations();
|
||||
requireActivity().runOnUiThread(this::loadConversations);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -455,10 +484,12 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
if (!isAdded()) {
|
||||
return;
|
||||
}
|
||||
loadConversations();
|
||||
if (activeConversationId != null) {
|
||||
loadMessageHistory(activeConversationId);
|
||||
}
|
||||
requireActivity().runOnUiThread(() -> {
|
||||
loadConversations();
|
||||
if (activeConversationId != null) {
|
||||
loadMessageHistory(activeConversationId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -495,21 +526,23 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
||||
if (conversationId == null) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < chatList.size(); i++) {
|
||||
Chat existing = chatList.get(i);
|
||||
if (existing.getChatId().equals(String.valueOf(conversationId))) {
|
||||
Chat updated = new Chat(
|
||||
existing.getChatId(),
|
||||
existing.getCustomerName(),
|
||||
lastMessage,
|
||||
existing.getCustomerId(),
|
||||
existing.getStaffId()
|
||||
);
|
||||
chatList.set(i, updated);
|
||||
chatAdapter.notifyItemChanged(i);
|
||||
return;
|
||||
requireActivity().runOnUiThread(() -> {
|
||||
for (int i = 0; i < chatList.size(); i++) {
|
||||
Chat existing = chatList.get(i);
|
||||
if (existing.getChatId().equals(String.valueOf(conversationId))) {
|
||||
Chat updated = new Chat(
|
||||
existing.getChatId(),
|
||||
existing.getCustomerName(),
|
||||
lastMessage,
|
||||
existing.getCustomerId(),
|
||||
existing.getStaffId()
|
||||
);
|
||||
chatList.set(i, updated);
|
||||
chatAdapter.notifyItemChanged(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,8 +4,10 @@ import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -25,8 +27,9 @@ import com.example.petstoremobile.utils.FileUtils;
|
||||
import com.example.petstoremobile.utils.GlideUtils;
|
||||
import com.example.petstoremobile.utils.ImagePickerHelper;
|
||||
import com.example.petstoremobile.utils.InputValidator;
|
||||
import com.example.petstoremobile.utils.RetrofitUtils;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.UIUtils;
|
||||
import com.example.petstoremobile.viewmodels.AuthViewModel;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
@@ -48,9 +51,9 @@ public class ProfileFragment extends Fragment {
|
||||
|
||||
private FragmentProfileBinding binding;
|
||||
private UserDTO currentUser;
|
||||
private AuthViewModel viewModel;
|
||||
private boolean hasImage = false;
|
||||
|
||||
@Inject AuthApi authApi;
|
||||
@Inject TokenManager tokenManager;
|
||||
@Inject @Named("baseUrl") String baseUrl;
|
||||
|
||||
@@ -62,6 +65,7 @@ public class ProfileFragment extends Fragment {
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(AuthViewModel.class);
|
||||
|
||||
imagePickerHelper = new ImagePickerHelper(this, "profile_photo.jpg", new ImagePickerHelper.ImagePickerListener() {
|
||||
@Override
|
||||
@@ -178,31 +182,36 @@ public class ProfileFragment extends Fragment {
|
||||
* Fetches current user profile data from the API and then updates the UI.
|
||||
*/
|
||||
private void loadProfileData() {
|
||||
authApi.getMe().enqueue(RetrofitUtils.createCallback(requireContext(), "PROFILE", null, result -> {
|
||||
currentUser = result;
|
||||
viewModel.getMe().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
currentUser = resource.data;
|
||||
|
||||
//set the user data to the view
|
||||
binding.tvProfileName.setText(currentUser.getFullName());
|
||||
binding.tvProfileEmail.setText(currentUser.getEmail());
|
||||
binding.tvProfilePhone.setText(currentUser.getPhone());
|
||||
binding.tvProfileRole.setText(currentUser.getRole());
|
||||
//set the user data to the view
|
||||
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();
|
||||
// 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(), binding.imgProfile, avatarUrl, token, R.drawable.placeholder, new GlideUtils.ImageLoadListener() {
|
||||
@Override
|
||||
public void onResourceReady() {
|
||||
hasImage = true;
|
||||
}
|
||||
GlideUtils.loadImageWithToken(requireContext(), binding.imgProfile, avatarUrl, token, R.drawable.placeholder, new GlideUtils.ImageLoadListener() {
|
||||
@Override
|
||||
public void onResourceReady() {
|
||||
hasImage = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFailed() {
|
||||
hasImage = false;
|
||||
}
|
||||
});
|
||||
}));
|
||||
@Override
|
||||
public void onLoadFailed() {
|
||||
hasImage = false;
|
||||
}
|
||||
});
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Failed to load profile: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -218,11 +227,17 @@ public class ProfileFragment extends Fragment {
|
||||
MultipartBody.Part body = MultipartBody.Part.createFormData("avatar", file.getName(), requestFile);
|
||||
|
||||
//Call the backend to upload the avatar
|
||||
authApi.uploadAvatar(body).enqueue(RetrofitUtils.createCallback(requireContext(), "UPLOAD_AVATAR", "Avatar updated successfully", result -> {
|
||||
currentUser = result;
|
||||
// Reload image after successful upload
|
||||
loadProfileData();
|
||||
}));
|
||||
viewModel.uploadAvatar(body).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
currentUser = resource.data;
|
||||
Toast.makeText(getContext(), "Avatar updated successfully", Toast.LENGTH_SHORT).show();
|
||||
// Reload image after successful upload
|
||||
loadProfileData();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Upload failed: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Log.e("UPLOAD_AVATAR", "Error: " + e.getMessage());
|
||||
}
|
||||
@@ -232,10 +247,16 @@ public class ProfileFragment extends Fragment {
|
||||
* Sends a request to the API to delete the current user's avatar image.
|
||||
*/
|
||||
private void deleteAvatar() {
|
||||
authApi.deleteAvatar().enqueue(RetrofitUtils.createCallback(requireContext(), "DELETE_AVATAR", "Avatar removed successfully", result -> {
|
||||
hasImage = false;
|
||||
binding.imgProfile.setImageResource(R.drawable.placeholder);
|
||||
}));
|
||||
viewModel.deleteAvatar().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
hasImage = false;
|
||||
binding.imgProfile.setImageResource(R.drawable.placeholder);
|
||||
Toast.makeText(getContext(), "Avatar removed successfully", Toast.LENGTH_SHORT).show();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Removal failed: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -245,11 +266,17 @@ public class ProfileFragment extends Fragment {
|
||||
Map<String, String> updates = new HashMap<>();
|
||||
updates.put(fieldName, value);
|
||||
|
||||
authApi.updateMe(updates).enqueue(RetrofitUtils.createCallback(requireContext(), "UPDATE_PROFILE", "Profile updated successfully", result -> {
|
||||
currentUser = result;
|
||||
// Update the view with the new data from backend
|
||||
binding.tvProfileEmail.setText(currentUser.getEmail());
|
||||
binding.tvProfilePhone.setText(currentUser.getPhone());
|
||||
}));
|
||||
viewModel.updateMe(updates).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
currentUser = resource.data;
|
||||
Toast.makeText(getContext(), "Profile updated successfully", Toast.LENGTH_SHORT).show();
|
||||
// Update the view with the new data from backend
|
||||
binding.tvProfileEmail.setText(currentUser.getEmail());
|
||||
binding.tvProfilePhone.setText(currentUser.getPhone());
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Update failed: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,10 +239,6 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop
|
||||
if (position != -1) {
|
||||
AdoptionDTO a = filteredList.get(position);
|
||||
args.putLong("adoptionId", a.getAdoptionId());
|
||||
args.putLong("petId", a.getPetId() != null ? a.getPetId() : -1);
|
||||
args.putLong("customerId", a.getCustomerId() != null ? a.getCustomerId() : -1);
|
||||
args.putString("adoptionDate", a.getAdoptionDate());
|
||||
args.putString("adoptionStatus", a.getAdoptionStatus());
|
||||
}
|
||||
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_adoption_detail, args);
|
||||
|
||||
@@ -22,13 +22,8 @@ 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;
|
||||
import com.example.petstoremobile.fragments.ListFragment;
|
||||
import com.example.petstoremobile.viewmodels.AppointmentViewModel;
|
||||
import com.example.petstoremobile.viewmodels.PetViewModel;
|
||||
import com.example.petstoremobile.viewmodels.ServiceViewModel;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.EventDecorator;
|
||||
import com.prolificinteractive.materialcalendarview.CalendarDay;
|
||||
import com.prolificinteractive.materialcalendarview.CalendarMode;
|
||||
@@ -50,13 +45,9 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
private FragmentAppointmentBinding binding;
|
||||
private List<AppointmentDTO> appointmentList = new ArrayList<>();
|
||||
private List<AppointmentDTO> filteredList = new ArrayList<>();
|
||||
private List<PetDTO> petList = new ArrayList<>();
|
||||
private List<ServiceDTO> serviceList = new ArrayList<>();
|
||||
|
||||
private AppointmentAdapter adapter;
|
||||
private AppointmentViewModel appointmentViewModel;
|
||||
private PetViewModel petViewModel;
|
||||
private ServiceViewModel serviceViewModel;
|
||||
|
||||
private CalendarDay selectedCalendarDay;
|
||||
private boolean isMonthMode = false;
|
||||
@@ -69,8 +60,6 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
appointmentViewModel = new ViewModelProvider(this).get(AppointmentViewModel.class);
|
||||
petViewModel = new ViewModelProvider(this).get(PetViewModel.class);
|
||||
serviceViewModel = new ViewModelProvider(this).get(ServiceViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,8 +75,6 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
setupSwipeRefresh();
|
||||
setupCalendar();
|
||||
loadAppointmentData();
|
||||
loadPets();
|
||||
loadServices();
|
||||
|
||||
binding.fabAddAppointment.setOnClickListener(v -> openAppointmentDetails(-1));
|
||||
|
||||
@@ -226,37 +213,13 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
*/
|
||||
private void openAppointmentDetails(int position) {
|
||||
Bundle args = new Bundle();
|
||||
|
||||
if (position != -1) {
|
||||
AppointmentDTO a = filteredList.get(position);
|
||||
args.putLong("appointmentId", a.getAppointmentId());
|
||||
args.putString("appointmentDate", a.getAppointmentDate());
|
||||
args.putString("appointmentTime", a.getAppointmentTime());
|
||||
args.putString("appointmentStatus", a.getAppointmentStatus());
|
||||
// IDs for pre-selecting spinners
|
||||
if (a.getPetID() != null) args.putLong("petId", a.getPetID());
|
||||
if (a.getServiceId() != null) args.putLong("serviceId", a.getServiceId());
|
||||
if (a.getCustomerId() != null) args.putLong("customerId", a.getCustomerId());
|
||||
if (a.getStoreId() != null) args.putLong("storeId", a.getStoreId());
|
||||
}
|
||||
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_appointment_detail, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads data when an appointment is saved.
|
||||
*/
|
||||
public void onAppointmentSaved(int position, AppointmentDTO appointment) {
|
||||
loadAppointmentData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads data when an appointment is deleted.
|
||||
*/
|
||||
public void onAppointmentDeleted(int position) {
|
||||
loadAppointmentData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles item click in the appointment list.
|
||||
*/
|
||||
@@ -299,30 +262,6 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter.
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the full list of pets from the server.
|
||||
*/
|
||||
private void loadPets() {
|
||||
petViewModel.getAllPets(0, 100).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
petList.clear();
|
||||
petList.addAll(resource.data.getContent());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the full list of services from the server.
|
||||
*/
|
||||
private void loadServices() {
|
||||
serviceViewModel.getAllServices(0, 100).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
serviceList.clear();
|
||||
serviceList.addAll(resource.data.getContent());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the RecyclerView for displaying appointments.
|
||||
*/
|
||||
|
||||
@@ -341,10 +341,6 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
|
||||
|
||||
if (inv != null) {
|
||||
args.putLong("inventoryId", inv.getInventoryId());
|
||||
args.putLong("prodId", inv.getProdId() != null ? inv.getProdId() : -1);
|
||||
args.putString("productName", inv.getProductName());
|
||||
args.putString("categoryName", inv.getCategoryName());
|
||||
args.putInt("quantity", inv.getQuantity() != null ? inv.getQuantity() : 0);
|
||||
}
|
||||
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_inventory_detail, args);
|
||||
|
||||
@@ -166,18 +166,7 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen
|
||||
private void openPetProfile(int position) {
|
||||
Bundle args = new Bundle();
|
||||
PetDTO pet = filteredList.get(position);
|
||||
args.putInt("petId", pet.getPetId().intValue());
|
||||
args.putString("petName", pet.getPetName());
|
||||
args.putString("petSpecies", pet.getPetSpecies());
|
||||
args.putString("petBreed", pet.getPetBreed());
|
||||
args.putInt("petAge", pet.getPetAge());
|
||||
args.putString("petStatus", pet.getPetStatus());
|
||||
|
||||
try {
|
||||
args.putDouble("petPrice", Double.parseDouble(pet.getPetPrice()));
|
||||
} catch (Exception e) {
|
||||
args.putDouble("petPrice", 0.0);
|
||||
}
|
||||
args.putLong("petId", pet.getPetId());
|
||||
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_pet_profile, args);
|
||||
}
|
||||
|
||||
@@ -170,10 +170,6 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc
|
||||
if (position != -1) {
|
||||
ProductDTO p = filteredList.get(position);
|
||||
args.putLong("prodId", p.getProdId());
|
||||
args.putString("prodName", p.getProdName());
|
||||
args.putString("prodDesc", p.getProdDesc() != null ? p.getProdDesc() : "");
|
||||
args.putString("prodPrice", p.getProdPrice() != null ? p.getProdPrice().toString() : "");
|
||||
args.putLong("categoryId", p.getCategoryId() != null ? p.getCategoryId() : -1);
|
||||
}
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_product_detail, args);
|
||||
}
|
||||
|
||||
@@ -166,9 +166,6 @@ public class ProductSupplierFragment extends Fragment
|
||||
ProductSupplierDTO ps = filteredList.get(position);
|
||||
args.putLong("productId", ps.getProductId());
|
||||
args.putLong("supplierId", ps.getSupplierId());
|
||||
args.putString("productName", ps.getProductName());
|
||||
args.putString("supplierName", ps.getSupplierName());
|
||||
args.putString("cost", ps.getCost() != null ? ps.getCost().toString() : "");
|
||||
}
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_product_supplier_detail, args);
|
||||
}
|
||||
|
||||
@@ -166,9 +166,6 @@ public class PurchaseOrderFragment extends Fragment
|
||||
Bundle args = new Bundle();
|
||||
PurchaseOrderDTO po = filteredList.get(position);
|
||||
args.putLong("purchaseOrderId", po.getPurchaseOrderId());
|
||||
args.putString("supplierName", po.getSupplierName());
|
||||
args.putString("orderDate", po.getOrderDate());
|
||||
args.putString("status", po.getStatus());
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_purchase_order_detail, args);
|
||||
}
|
||||
|
||||
|
||||
@@ -128,16 +128,11 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic
|
||||
private void openServiceDetails(int position) {
|
||||
//Make a bundle to pass data to the detail fragment
|
||||
Bundle args = new Bundle();
|
||||
args.putInt("position", position);
|
||||
|
||||
//if editing a service, add the service data to the bundle
|
||||
//if editing a service, add the service id to the bundle
|
||||
if (position != -1) {
|
||||
ServiceDTO service = filteredList.get(position);
|
||||
args.putInt("serviceId", service.getServiceId().intValue());
|
||||
args.putString("serviceName", service.getServiceName());
|
||||
args.putString("serviceDesc", service.getServiceDesc());
|
||||
args.putInt("serviceDuration", service.getServiceDuration());
|
||||
args.putDouble("servicePrice", service.getServicePrice());
|
||||
args.putLong("serviceId", service.getServiceId());
|
||||
}
|
||||
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_service_detail, args);
|
||||
|
||||
@@ -129,17 +129,11 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp
|
||||
private void openSupplierDetails(int position) {
|
||||
//Make a bundle to pass data to the detail fragment
|
||||
Bundle args = new Bundle();
|
||||
args.putInt("position", position);
|
||||
|
||||
//if editing a supplier, add the supplier data to the bundle
|
||||
//if editing a supplier, add the supplier id to the bundle
|
||||
if (position != -1) {
|
||||
SupplierDTO supplier = filteredList.get(position);
|
||||
args.putInt("supId", supplier.getSupId().intValue());
|
||||
args.putString("supCompany", supplier.getSupCompany());
|
||||
args.putString("supContactFirstName", supplier.getSupContactFirstName());
|
||||
args.putString("supContactLastName", supplier.getSupContactLastName());
|
||||
args.putString("supEmail", supplier.getSupEmail());
|
||||
args.putString("supPhone", supplier.getSupPhone());
|
||||
args.putLong("supId", supplier.getSupId());
|
||||
}
|
||||
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_supplier_detail, args);
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.os.Bundle;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
@@ -56,15 +57,20 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
binding = FragmentAdoptionDetailBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
setupSpinners();
|
||||
setupDatePicker();
|
||||
loadData();
|
||||
loadSpinnersData();
|
||||
handleArguments();
|
||||
|
||||
binding.btnAdoptionBack.setOnClickListener(v -> navigateBack());
|
||||
binding.btnSaveAdoption.setOnClickListener(v -> saveAdoption());
|
||||
binding.btnDeleteAdoption.setOnClickListener(v -> confirmDelete());
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -96,9 +102,9 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches required data (pets and customers) from the backend.
|
||||
* Fetches required data for spinners from the backend.
|
||||
*/
|
||||
private void loadData() {
|
||||
private void loadSpinnersData() {
|
||||
loadPets();
|
||||
loadCustomers();
|
||||
}
|
||||
@@ -110,13 +116,20 @@ 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(), binding.spinnerAdoptionPet, petList,
|
||||
PetDTO::getPetName, "-- Select Pet --",
|
||||
preselectedPetId, PetDTO::getPetId);
|
||||
refreshPetSpinner();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the pet selection spinner with data.
|
||||
*/
|
||||
private void refreshPetSpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerAdoptionPet, petList,
|
||||
PetDTO::getPetName, "-- Select Pet --",
|
||||
preselectedPetId, PetDTO::getPetId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the list of customers from the API.
|
||||
*/
|
||||
@@ -124,14 +137,21 @@ 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(), binding.spinnerAdoptionCustomer, customerList,
|
||||
item -> item.getFirstName() + " " + item.getLastName(),
|
||||
"-- Select Customer --",
|
||||
preselectedCustomerId, CustomerDTO::getCustomerId);
|
||||
refreshCustomerSpinner();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the customer selection spinner with data.
|
||||
*/
|
||||
private void refreshCustomerSpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerAdoptionCustomer, customerList,
|
||||
item -> item.getFirstName() + " " + item.getLastName(),
|
||||
"-- Select Customer --",
|
||||
preselectedCustomerId, CustomerDTO::getCustomerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles arguments to determine if the fragment is in edit or add mode.
|
||||
*/
|
||||
@@ -139,18 +159,12 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
Bundle a = getArguments();
|
||||
if (a != null && a.containsKey("adoptionId")) {
|
||||
isEditing = true;
|
||||
adoptionId = a.getLong("adoptionId");
|
||||
preselectedPetId = a.getLong("petId", -1);
|
||||
preselectedCustomerId = a.getLong("customerId", -1);
|
||||
|
||||
adoptionId = a.getLong("adoptionId");
|
||||
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(binding.spinnerAdoptionStatus, a.getString("adoptionStatus", "Pending"));
|
||||
loadAdoptionData();
|
||||
} else {
|
||||
binding.tvAdoptionMode.setText("Add Adoption");
|
||||
binding.btnDeleteAdoption.setVisibility(View.GONE);
|
||||
@@ -158,6 +172,27 @@ public class AdoptionDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches specific adoption details from the backend using the ID.
|
||||
*/
|
||||
private void loadAdoptionData() {
|
||||
adoptionViewModel.getAdoptionById(adoptionId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
AdoptionDTO a = resource.data;
|
||||
preselectedPetId = a.getPetId() != null ? a.getPetId() : -1;
|
||||
preselectedCustomerId = a.getCustomerId() != null ? a.getCustomerId() : -1;
|
||||
binding.etAdoptionDate.setText(a.getAdoptionDate());
|
||||
SpinnerUtils.setSelectionByValue(binding.spinnerAdoptionStatus, a.getAdoptionStatus());
|
||||
|
||||
refreshPetSpinner();
|
||||
refreshCustomerSpinner();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Failed to load adoption: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates input and saves the adoption request to the backend.
|
||||
*/
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.util.Log;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
@@ -68,15 +69,20 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
binding = FragmentAppointmentDetailBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
setupSpinners();
|
||||
setupDatePicker();
|
||||
loadData();
|
||||
loadSpinnersData();
|
||||
handleArguments();
|
||||
|
||||
binding.btnApptBack.setOnClickListener(v -> navigateBack());
|
||||
binding.btnSaveAppointment.setOnClickListener(v -> saveAppointment());
|
||||
binding.btnDeleteAppointment.setOnClickListener(v -> confirmDelete());
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -115,9 +121,9 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches all required data from the backend to populate the fragment.
|
||||
* Fetches all required data for spinners from the backend.
|
||||
*/
|
||||
private void loadData() {
|
||||
private void loadSpinnersData() {
|
||||
loadPets();
|
||||
loadServices();
|
||||
loadCustomers();
|
||||
@@ -131,13 +137,20 @@ 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(), binding.spinnerPet, petList,
|
||||
PetDTO::getPetName, "-- Select Pet --",
|
||||
preselectedPetId, PetDTO::getPetId);
|
||||
refreshPetSpinner();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the pet selection spinner.
|
||||
*/
|
||||
private void refreshPetSpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerPet, petList,
|
||||
PetDTO::getPetName, "-- Select Pet --",
|
||||
preselectedPetId, PetDTO::getPetId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the list of services from the API.
|
||||
*/
|
||||
@@ -145,13 +158,20 @@ 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(), binding.spinnerService, serviceList,
|
||||
ServiceDTO::getServiceName, "-- Select Service --",
|
||||
preselectedServiceId, ServiceDTO::getServiceId);
|
||||
refreshServiceSpinner();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the service selection spinner.
|
||||
*/
|
||||
private void refreshServiceSpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerService, serviceList,
|
||||
ServiceDTO::getServiceName, "-- Select Service --",
|
||||
preselectedServiceId, ServiceDTO::getServiceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the list of customers from the API.
|
||||
*/
|
||||
@@ -159,14 +179,21 @@ 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(), binding.spinnerCustomer, customerList,
|
||||
item -> item.getFirstName() + " " + item.getLastName(),
|
||||
"-- Select Customer --",
|
||||
preselectedCustomerId, CustomerDTO::getCustomerId);
|
||||
refreshCustomerSpinner();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the customer selection spinner.
|
||||
*/
|
||||
private void refreshCustomerSpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerCustomer, customerList,
|
||||
item -> item.getFirstName() + " " + item.getLastName(),
|
||||
"-- Select Customer --",
|
||||
preselectedCustomerId, CustomerDTO::getCustomerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the list of stores from the API.
|
||||
*/
|
||||
@@ -174,13 +201,20 @@ 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(), binding.spinnerStore, storeList,
|
||||
StoreDTO::getStoreName, "-- Select Store --",
|
||||
preselectedStoreId, StoreDTO::getStoreId);
|
||||
refreshStoreSpinner();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the store selection spinner.
|
||||
*/
|
||||
private void refreshStoreSpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerStore, storeList,
|
||||
StoreDTO::getStoreName, "-- Select Store --",
|
||||
preselectedStoreId, StoreDTO::getStoreId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles arguments to determine if the fragment is in edit or add mode.
|
||||
*/
|
||||
@@ -188,34 +222,12 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
Bundle a = getArguments();
|
||||
if (a != null && a.containsKey("appointmentId")) {
|
||||
isEditing = true;
|
||||
appointmentId = a.getLong("appointmentId");
|
||||
preselectedPetId = a.getLong("petId", -1);
|
||||
preselectedServiceId= a.getLong("serviceId", -1);
|
||||
preselectedCustomerId = a.getLong("customerId", -1);
|
||||
preselectedStoreId = a.getLong("storeId", -1);
|
||||
|
||||
appointmentId = a.getLong("appointmentId");
|
||||
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");
|
||||
if (time.length() > 5) time = time.substring(0, 5);
|
||||
String[] parts = time.split(":");
|
||||
if (parts.length == 2) {
|
||||
int hour = Integer.parseInt(parts[0]);
|
||||
int min = Integer.parseInt(parts[1]);
|
||||
for (int i = 0; i < HOURS.length; i++)
|
||||
if (HOURS[i] == hour) { binding.spinnerHour.setSelection(i); break; }
|
||||
for (int i = 0; i < MINUTES.length; i++)
|
||||
if (MINUTES[i] == min) { binding.spinnerMinute.setSelection(i); break; }
|
||||
}
|
||||
|
||||
// Pre-fill status
|
||||
SpinnerUtils.setSelectionByValue(binding.spinnerAppointmentStatus, a.getString("appointmentStatus", "Booked"));
|
||||
|
||||
loadAppointmentData();
|
||||
} else {
|
||||
binding.tvApptMode.setText("Add Appointment");
|
||||
binding.btnDeleteAppointment.setVisibility(View.GONE);
|
||||
@@ -223,6 +235,48 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches specific appointment details from the backend using the ID.
|
||||
*/
|
||||
private void loadAppointmentData() {
|
||||
appointmentViewModel.getAppointmentById(appointmentId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
AppointmentDTO a = resource.data;
|
||||
preselectedPetId = (a.getPetID() != null) ? a.getPetID() : -1;
|
||||
preselectedServiceId = (a.getServiceId() != null) ? a.getServiceId() : -1;
|
||||
preselectedCustomerId = (a.getCustomerId() != null) ? a.getCustomerId() : -1;
|
||||
preselectedStoreId = (a.getStoreId() != null) ? a.getStoreId() : -1;
|
||||
|
||||
binding.etAppointmentDate.setText(a.getAppointmentDate());
|
||||
|
||||
// Pre-fill time spinners
|
||||
String time = a.getAppointmentTime() != null ? a.getAppointmentTime() : "09:00";
|
||||
if (time.length() > 5) time = time.substring(0, 5);
|
||||
String[] parts = time.split(":");
|
||||
if (parts.length == 2) {
|
||||
try {
|
||||
int hour = Integer.parseInt(parts[0]);
|
||||
int min = Integer.parseInt(parts[1]);
|
||||
for (int i = 0; i < HOURS.length; i++)
|
||||
if (HOURS[i] == hour) { binding.spinnerHour.setSelection(i); break; }
|
||||
for (int i = 0; i < MINUTES.length; i++)
|
||||
if (MINUTES[i] == min) { binding.spinnerMinute.setSelection(i); break; }
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
|
||||
SpinnerUtils.setSelectionByValue(binding.spinnerAppointmentStatus, a.getAppointmentStatus());
|
||||
|
||||
refreshPetSpinner();
|
||||
refreshServiceSpinner();
|
||||
refreshCustomerSpinner();
|
||||
refreshStoreSpinner();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Failed to load appointment: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates input and saves the appointment to the backend.
|
||||
*/
|
||||
@@ -307,6 +361,9 @@ public class AppointmentDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles errors that occur during the saving process.
|
||||
*/
|
||||
private void handleSaveError(String errorMessage) {
|
||||
if (errorMessage != null) {
|
||||
Log.e("APPT_SAVE", "Error: " + errorMessage);
|
||||
|
||||
@@ -12,6 +12,7 @@ import android.widget.ArrayAdapter;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
@@ -19,6 +20,7 @@ import androidx.navigation.fragment.NavHostFragment;
|
||||
|
||||
import com.example.petstoremobile.adapters.BlackTextArrayAdapter;
|
||||
import com.example.petstoremobile.databinding.FragmentInventoryDetailBinding;
|
||||
import com.example.petstoremobile.dtos.InventoryDTO;
|
||||
import com.example.petstoremobile.dtos.InventoryRequest;
|
||||
import com.example.petstoremobile.dtos.ProductDTO;
|
||||
import com.example.petstoremobile.utils.InputValidator;
|
||||
@@ -56,6 +58,9 @@ public class InventoryDetailFragment extends Fragment {
|
||||
private final List<ProductDTO> productSuggestions = new ArrayList<>();
|
||||
private ArrayAdapter<String> dropdownAdapter;
|
||||
|
||||
/**
|
||||
* Initializes the view models.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -63,10 +68,22 @@ public class InventoryDetailFragment extends Fragment {
|
||||
productViewModel = new ViewModelProvider(this).get(ProductViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
binding = FragmentInventoryDetailBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up UI components after the view is created.
|
||||
*/
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
setupProductSearch();
|
||||
handleArguments();
|
||||
@@ -80,27 +97,26 @@ public class InventoryDetailFragment extends Fragment {
|
||||
android.R.layout.simple_dropdown_item_1line, new ArrayList<>());
|
||||
binding.etProductSearch.setAdapter(dropdownAdapter);
|
||||
binding.etProductSearch.setThreshold(1); // start showing after 1 character
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
if (searchRunnable != null) {
|
||||
searchHandler.removeCallbacks(searchRunnable);
|
||||
}
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* setup the product search dropdown.
|
||||
* Sets up the product search dropdown.
|
||||
*/
|
||||
private void setupProductSearch() {
|
||||
binding.etProductSearch.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int i, int i1, int i2) {
|
||||
@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
|
||||
@@ -137,6 +153,7 @@ public class InventoryDetailFragment extends Fragment {
|
||||
* Searches for products matching the query from the backend.
|
||||
*/
|
||||
private void searchProducts(String query) {
|
||||
if (getView() == null) return;
|
||||
productViewModel.getAllProducts(query, 0, 20).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
productSuggestions.clear();
|
||||
@@ -157,7 +174,7 @@ public class InventoryDetailFragment extends Fragment {
|
||||
}
|
||||
|
||||
/**
|
||||
* arguments to set up edit or add mode.
|
||||
* Handles fragment arguments to determine if we are in edit or add mode.
|
||||
*/
|
||||
private void handleArguments() {
|
||||
Bundle args = getArguments();
|
||||
@@ -168,27 +185,10 @@ public class InventoryDetailFragment extends Fragment {
|
||||
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);
|
||||
binding.etProductSearch.setText(productName);
|
||||
|
||||
// Show existing product info
|
||||
if (prodId != -1) {
|
||||
binding.tvProductInfo.setText(
|
||||
"ID: " + prodId
|
||||
+ " • " + args.getString("categoryName", ""));
|
||||
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);
|
||||
}
|
||||
|
||||
binding.etQuantity.setText(String.valueOf(args.getInt("quantity", 0)));
|
||||
binding.btnDeleteInventory.setVisibility(View.VISIBLE);
|
||||
binding.btnSaveInventory.setText("Save");
|
||||
|
||||
loadInventoryData();
|
||||
} else {
|
||||
isEditing = false;
|
||||
binding.tvInventoryMode.setText("Add Inventory");
|
||||
@@ -200,7 +200,35 @@ public class InventoryDetailFragment extends Fragment {
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the current inventory item details to the backend.
|
||||
* Loads existing inventory data from the backend.
|
||||
*/
|
||||
private void loadInventoryData() {
|
||||
inventoryViewModel.getInventoryById(inventoryId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
InventoryDTO inv = resource.data;
|
||||
binding.etProductSearch.setText(inv.getProductName());
|
||||
binding.etQuantity.setText(String.valueOf(inv.getQuantity()));
|
||||
|
||||
if (inv.getProdId() != null) {
|
||||
binding.tvProductInfo.setText(
|
||||
"ID: " + inv.getProdId()
|
||||
+ " • " + inv.getCategoryName());
|
||||
binding.tvProductInfo.setVisibility(View.VISIBLE);
|
||||
|
||||
selectedProduct = new ProductDTO();
|
||||
selectedProduct.setProdId(inv.getProdId());
|
||||
selectedProduct.setProdName(inv.getProductName());
|
||||
selectedProduct.setCategoryName(inv.getCategoryName());
|
||||
}
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Failed to load inventory: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates input and saves the current inventory item details to the backend.
|
||||
*/
|
||||
private void saveInventory() {
|
||||
if (selectedProduct == null) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||
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;
|
||||
@@ -31,7 +32,7 @@ import dagger.hilt.android.AndroidEntryPoint;
|
||||
public class PetDetailFragment extends Fragment {
|
||||
|
||||
private FragmentPetDetailBinding binding;
|
||||
private int petId;
|
||||
private long petId;
|
||||
private boolean isEditing = false;
|
||||
|
||||
private PetViewModel viewModel;
|
||||
@@ -46,6 +47,12 @@ public class PetDetailFragment extends Fragment {
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
binding = FragmentPetDetailBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
setupSpinner();
|
||||
handleArguments();
|
||||
@@ -54,8 +61,6 @@ public class PetDetailFragment extends Fragment {
|
||||
binding.btnBack.setOnClickListener(v -> navigateBack());
|
||||
binding.btnSavePet.setOnClickListener(v -> savePet());
|
||||
binding.btnDeletePet.setOnClickListener(v -> deletePet());
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,10 +100,10 @@ public class PetDetailFragment extends Fragment {
|
||||
//check if the pet is being edited or added
|
||||
if (isEditing) {
|
||||
// Update existing pet
|
||||
petDTO.setPetId((long) petId);
|
||||
viewModel.updatePet((long) petId, petDTO).observe(getViewLifecycleOwner(), resource -> {
|
||||
petDTO.setPetId(petId);
|
||||
viewModel.updatePet(petId, petDTO).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
ActivityLogger.logChange(requireContext(), "Pet", "UPDATED", petId);
|
||||
ActivityLogger.logChange(requireContext(), "Pet", "UPDATED", (int) petId);
|
||||
Toast.makeText(getContext(), "Pet updated successfully!", Toast.LENGTH_SHORT).show();
|
||||
navigateToPetList();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
@@ -124,9 +129,9 @@ public class PetDetailFragment extends Fragment {
|
||||
*/
|
||||
private void deletePet() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Pet", () ->
|
||||
viewModel.deletePet((long) petId).observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.deletePet(petId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
ActivityLogger.logChange(requireContext(), "Pet", "DELETED", petId);
|
||||
ActivityLogger.logChange(requireContext(), "Pet", "DELETED", (int) petId);
|
||||
Toast.makeText(getContext(), "Pet deleted successfully!", Toast.LENGTH_SHORT).show();
|
||||
navigateToPetList();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
@@ -157,16 +162,12 @@ public class PetDetailFragment extends Fragment {
|
||||
if (getArguments() != null && getArguments().containsKey("petId")) {
|
||||
// Get pet data from arguments and populate fields
|
||||
isEditing = true;
|
||||
petId = getArguments().getInt("petId");
|
||||
petId = getArguments().getLong("petId");
|
||||
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.tvPetId.setVisibility(View.VISIBLE);
|
||||
binding.btnDeletePet.setVisibility(View.VISIBLE);
|
||||
loadPetData();
|
||||
} else {
|
||||
// Pet is being added
|
||||
// Set default values for add a new pet
|
||||
@@ -178,6 +179,26 @@ public class PetDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches specific pet details from the backend using the ID.
|
||||
*/
|
||||
private void loadPetData() {
|
||||
viewModel.getPetById(petId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
PetDTO p = resource.data;
|
||||
binding.etPetName.setText(p.getPetName());
|
||||
binding.etPetSpecies.setText(p.getPetSpecies());
|
||||
binding.etPetBreed.setText(p.getPetBreed());
|
||||
binding.etPetAge.setText(String.valueOf(p.getPetAge()));
|
||||
binding.etPetPrice.setText(p.getPetPrice());
|
||||
SpinnerUtils.setSelectionByValue(binding.spinnerPetStatus, p.getPetStatus());
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Failed to load pet: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the spinner for pet status selection.
|
||||
*/
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.os.Bundle;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
@@ -21,6 +22,7 @@ import com.example.petstoremobile.utils.FileUtils;
|
||||
import com.example.petstoremobile.utils.GlideUtils;
|
||||
import com.example.petstoremobile.utils.ImagePickerHelper;
|
||||
import com.example.petstoremobile.utils.InputValidator;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.SpinnerUtils;
|
||||
|
||||
import java.io.File;
|
||||
@@ -89,12 +91,21 @@ public class ProductDetailFragment extends Fragment {
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout and initializes UI components and listeners.
|
||||
* Inflates the layout.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
binding = FragmentProductDetailBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up UI components and listeners after the view is created.
|
||||
*/
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
loadCategories();
|
||||
handleArguments();
|
||||
@@ -103,7 +114,6 @@ public class ProductDetailFragment extends Fragment {
|
||||
binding.btnSaveProduct.setOnClickListener(v -> saveProduct());
|
||||
binding.btnDeleteProduct.setOnClickListener(v -> confirmDelete());
|
||||
binding.ivProductImage.setOnClickListener(v -> imagePickerHelper.showImagePickerDialog("Select Product Image", hasImage));
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -117,7 +127,7 @@ public class ProductDetailFragment extends Fragment {
|
||||
*/
|
||||
private void loadCategories() {
|
||||
viewModel.getAllCategories(0, 100).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == com.example.petstoremobile.utils.Resource.Status.SUCCESS && resource.data != null) {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
categoryList = resource.data.getContent();
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerProductCategory, categoryList,
|
||||
CategoryDTO::getCategoryName, "-- Select Category --",
|
||||
@@ -134,16 +144,11 @@ public class ProductDetailFragment extends Fragment {
|
||||
if (a != null && a.containsKey("prodId")) {
|
||||
isEditing = true;
|
||||
prodId = a.getLong("prodId");
|
||||
preselectedCategoryId = a.getLong("categoryId", -1);
|
||||
hasImage = true;
|
||||
|
||||
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);
|
||||
loadProductData();
|
||||
loadProductImage();
|
||||
} else {
|
||||
binding.tvProductMode.setText("Add Product");
|
||||
@@ -153,6 +158,31 @@ public class ProductDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the product data from the backend.
|
||||
*/
|
||||
private void loadProductData() {
|
||||
viewModel.getProductById(prodId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
ProductDTO p = resource.data;
|
||||
binding.etProductName.setText(p.getProdName());
|
||||
binding.etProductDesc.setText(p.getProdDesc());
|
||||
binding.etProductPrice.setText(p.getProdPrice() != null ? p.getProdPrice().toString() : "");
|
||||
preselectedCategoryId = p.getCategoryId() != null ? p.getCategoryId() : -1;
|
||||
|
||||
// Refresh spinner selection once data is loaded
|
||||
if (!categoryList.isEmpty()) {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerProductCategory, categoryList,
|
||||
CategoryDTO::getCategoryName, "-- Select Category --",
|
||||
preselectedCategoryId, CategoryDTO::getCategoryId);
|
||||
}
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Failed to load product: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the product image from the backend.
|
||||
*/
|
||||
@@ -179,8 +209,8 @@ public class ProductDetailFragment extends Fragment {
|
||||
private void performPendingImageActions(String successMsg) {
|
||||
if (isImageRemoved) {
|
||||
viewModel.deleteProductImage(prodId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status != com.example.petstoremobile.utils.Resource.Status.LOADING) {
|
||||
if (resource.status == com.example.petstoremobile.utils.Resource.Status.SUCCESS) {
|
||||
if (resource != null && resource.status != Resource.Status.LOADING) {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), successMsg, Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(getContext(), successMsg + " (but image removal failed)", Toast.LENGTH_SHORT).show();
|
||||
@@ -211,8 +241,8 @@ public class ProductDetailFragment extends Fragment {
|
||||
MultipartBody.Part body = MultipartBody.Part.createFormData("image", file.getName(), requestFile);
|
||||
|
||||
viewModel.uploadProductImage(prodId, body).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status != com.example.petstoremobile.utils.Resource.Status.LOADING) {
|
||||
if (resource.status == com.example.petstoremobile.utils.Resource.Status.SUCCESS) {
|
||||
if (resource != null && resource.status != Resource.Status.LOADING) {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), successMsg, Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(getContext(), successMsg + " (but image upload failed)", Toast.LENGTH_SHORT).show();
|
||||
@@ -246,8 +276,8 @@ public class ProductDetailFragment extends Fragment {
|
||||
|
||||
if (isEditing) {
|
||||
viewModel.updateProduct(prodId, dto).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status != com.example.petstoremobile.utils.Resource.Status.LOADING) {
|
||||
if (resource.status == com.example.petstoremobile.utils.Resource.Status.SUCCESS) {
|
||||
if (resource != null && resource.status != Resource.Status.LOADING) {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
performPendingImageActions("Updated");
|
||||
} else {
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
@@ -256,8 +286,8 @@ public class ProductDetailFragment extends Fragment {
|
||||
});
|
||||
} else {
|
||||
viewModel.createProduct(dto).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status != com.example.petstoremobile.utils.Resource.Status.LOADING) {
|
||||
if (resource.status == com.example.petstoremobile.utils.Resource.Status.SUCCESS && resource.data != null) {
|
||||
if (resource != null && resource.status != Resource.Status.LOADING) {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
prodId = resource.data.getProdId();
|
||||
performPendingImageActions("Saved");
|
||||
} else {
|
||||
@@ -274,9 +304,9 @@ public class ProductDetailFragment extends Fragment {
|
||||
private void confirmDelete() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Product", () ->
|
||||
viewModel.deleteProduct(prodId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status == com.example.petstoremobile.utils.Resource.Status.SUCCESS) {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS) {
|
||||
navigateBack();
|
||||
} else if (resource != null && resource.status == com.example.petstoremobile.utils.Resource.Status.ERROR) {
|
||||
} else if (resource != null && resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Delete failed: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.os.Bundle;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
@@ -56,13 +57,18 @@ public class ProductSupplierDetailFragment extends Fragment {
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
binding = FragmentProductSupplierDetailBinding.inflate(inflater, container, false);
|
||||
loadData();
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
loadSpinnersData();
|
||||
handleArguments();
|
||||
|
||||
binding.btnPSBack.setOnClickListener(v -> navigateBack());
|
||||
binding.btnSavePS.setOnClickListener(v -> save());
|
||||
binding.btnDeletePS.setOnClickListener(v -> confirmDelete());
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -74,7 +80,7 @@ public class ProductSupplierDetailFragment extends Fragment {
|
||||
/**
|
||||
* Fetches products and suppliers to populate the spinners.
|
||||
*/
|
||||
private void loadData() {
|
||||
private void loadSpinnersData() {
|
||||
loadProducts();
|
||||
loadSuppliers();
|
||||
}
|
||||
@@ -86,13 +92,17 @@ 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(), binding.spinnerPSProduct, productList,
|
||||
ProductDTO::getProdName, "-- Select Product --",
|
||||
preselectedProductId, ProductDTO::getProdId);
|
||||
refreshProductSpinner();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void refreshProductSpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerPSProduct, productList,
|
||||
ProductDTO::getProdName, "-- Select Product --",
|
||||
preselectedProductId, ProductDTO::getProdId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the list of suppliers from the API.
|
||||
*/
|
||||
@@ -100,33 +110,39 @@ 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(), binding.spinnerPSSupplier, supplierList,
|
||||
SupplierDTO::getSupCompany, "-- Select Supplier --",
|
||||
preselectedSupplierId, SupplierDTO::getSupId);
|
||||
refreshSupplierSpinner();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void refreshSupplierSpinner() {
|
||||
SpinnerUtils.populateSpinner(requireContext(), binding.spinnerPSSupplier, supplierList,
|
||||
SupplierDTO::getSupCompany, "-- Select Supplier --",
|
||||
preselectedSupplierId, SupplierDTO::getSupId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles arguments to determine if the fragment is in edit or add mode.
|
||||
*/
|
||||
private void handleArguments() {
|
||||
Bundle a = getArguments();
|
||||
if (a != null && a.containsKey("productId")) {
|
||||
if (a != null && a.containsKey("productId") && a.containsKey("supplierId")) {
|
||||
isEditing = true;
|
||||
editProductId = a.getLong("productId");
|
||||
editSupplierId = a.getLong("supplierId");
|
||||
preselectedProductId = editProductId;
|
||||
editProductId = a.getLong("productId");
|
||||
editSupplierId = a.getLong("supplierId");
|
||||
preselectedProductId = editProductId;
|
||||
preselectedSupplierId = editSupplierId;
|
||||
binding.etPSCost.setText(a.getString("cost"));
|
||||
|
||||
binding.tvPSMode.setText("Edit Product Supplier");
|
||||
binding.btnDeletePS.setVisibility(View.VISIBLE);
|
||||
|
||||
} else {
|
||||
binding.tvPSMode.setText("Add Product Supplier");
|
||||
binding.btnDeletePS.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates input and saves the product-supplier to the backend.
|
||||
*/
|
||||
|
||||
@@ -3,11 +3,18 @@ package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.view.*;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
|
||||
import com.example.petstoremobile.databinding.FragmentPurchaseOrderDetailBinding;
|
||||
import com.example.petstoremobile.dtos.PurchaseOrderDTO;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.viewmodels.PurchaseOrderViewModel;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
@@ -18,40 +25,72 @@ import dagger.hilt.android.AndroidEntryPoint;
|
||||
public class PurchaseOrderDetailFragment extends Fragment {
|
||||
|
||||
private FragmentPurchaseOrderDetailBinding binding;
|
||||
private PurchaseOrderViewModel viewModel;
|
||||
private long purchaseOrderId;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(PurchaseOrderViewModel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout, initializes views, and populates order data from arguments.
|
||||
* Inflates the layout.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
binding = FragmentPurchaseOrderDetailBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
Bundle a = getArguments();
|
||||
if (a != null) {
|
||||
binding.tvPODetailId.setText("PO #" + a.getLong("purchaseOrderId"));
|
||||
binding.tvPODetailSupplier.setText(a.getString("supplierName"));
|
||||
binding.tvPODetailDate.setText(a.getString("orderDate"));
|
||||
/**
|
||||
* Initializes views and populates order data from backend after the view is created.
|
||||
*/
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
String status = a.getString("status", "");
|
||||
binding.tvPODetailStatus.setText(status);
|
||||
switch (status) {
|
||||
case "Completed":
|
||||
binding.tvPODetailStatus.setTextColor(Color.parseColor("#4CAF50")); break;
|
||||
case "Pending":
|
||||
binding.tvPODetailStatus.setTextColor(Color.parseColor("#FF9800")); break;
|
||||
case "Cancelled":
|
||||
binding.tvPODetailStatus.setTextColor(Color.parseColor("#F44336")); break;
|
||||
default:
|
||||
binding.tvPODetailStatus.setTextColor(Color.parseColor("#9E9E9E")); break;
|
||||
}
|
||||
}
|
||||
handleArguments();
|
||||
|
||||
binding.btnPOBack.setOnClickListener(v -> {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
});
|
||||
}
|
||||
|
||||
return binding.getRoot();
|
||||
private void handleArguments() {
|
||||
Bundle a = getArguments();
|
||||
if (a != null && a.containsKey("purchaseOrderId")) {
|
||||
purchaseOrderId = a.getLong("purchaseOrderId");
|
||||
loadPurchaseOrderData();
|
||||
}
|
||||
}
|
||||
|
||||
private void loadPurchaseOrderData() {
|
||||
viewModel.getPurchaseOrderById(purchaseOrderId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
PurchaseOrderDTO po = resource.data;
|
||||
binding.tvPODetailId.setText("PO #" + po.getPurchaseOrderId());
|
||||
binding.tvPODetailSupplier.setText(po.getSupplierName());
|
||||
binding.tvPODetailDate.setText(po.getOrderDate());
|
||||
|
||||
String status = po.getStatus() != null ? po.getStatus() : "";
|
||||
binding.tvPODetailStatus.setText(status);
|
||||
switch (status) {
|
||||
case "Completed":
|
||||
binding.tvPODetailStatus.setTextColor(Color.parseColor("#4CAF50")); break;
|
||||
case "Pending":
|
||||
binding.tvPODetailStatus.setTextColor(Color.parseColor("#FF9800")); break;
|
||||
case "Cancelled":
|
||||
binding.tvPODetailStatus.setTextColor(Color.parseColor("#F44336")); break;
|
||||
default:
|
||||
binding.tvPODetailStatus.setTextColor(Color.parseColor("#9E9E9E")); break;
|
||||
}
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Failed to load order: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
|
||||
@@ -38,15 +39,18 @@ public class RefundDetailFragment extends Fragment {
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
binding = FragmentRefundDetailBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
setupSpinner();
|
||||
handleArguments();
|
||||
|
||||
binding.btnRefundBack.setOnClickListener(v -> goBack());
|
||||
binding.btnLoadSale.setOnClickListener(v -> loadSaleDetails());
|
||||
binding.btnProcessRefund.setOnClickListener(v -> processRefund());
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||
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;
|
||||
@@ -30,7 +31,7 @@ import dagger.hilt.android.AndroidEntryPoint;
|
||||
public class ServiceDetailFragment extends Fragment {
|
||||
|
||||
private FragmentServiceDetailBinding binding;
|
||||
private int serviceId;
|
||||
private long serviceId;
|
||||
private boolean isEditing = false;
|
||||
|
||||
private ServiceViewModel viewModel;
|
||||
@@ -45,6 +46,12 @@ public class ServiceDetailFragment extends Fragment {
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
binding = FragmentServiceDetailBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
//get controls from layout and display the view depending on the mode
|
||||
handleArguments();
|
||||
@@ -53,8 +60,6 @@ public class ServiceDetailFragment extends Fragment {
|
||||
binding.btnBack.setOnClickListener(v -> navigateBack());
|
||||
binding.btnSaveService.setOnClickListener(v -> saveService());
|
||||
binding.btnDeleteService.setOnClickListener(v -> deleteService());
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -89,10 +94,10 @@ public class ServiceDetailFragment extends Fragment {
|
||||
//check if the service is being edited or added
|
||||
if (isEditing) {
|
||||
// Update existing service
|
||||
serviceDTO.setServiceId((long) serviceId);
|
||||
viewModel.updateService((long) serviceId, serviceDTO).observe(getViewLifecycleOwner(), resource -> {
|
||||
serviceDTO.setServiceId(serviceId);
|
||||
viewModel.updateService(serviceId, serviceDTO).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
ActivityLogger.logChange(requireContext(), "Service", "UPDATED", serviceId);
|
||||
ActivityLogger.logChange(requireContext(), "Service", "UPDATED", (int) serviceId);
|
||||
Toast.makeText(getContext(), "Service updated successfully!", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
@@ -117,9 +122,9 @@ public class ServiceDetailFragment extends Fragment {
|
||||
*/
|
||||
private void deleteService() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Service", () ->
|
||||
viewModel.deleteService((long) serviceId).observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.deleteService(serviceId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
ActivityLogger.logChange(requireContext(), "Service", "DELETED", serviceId);
|
||||
ActivityLogger.logChange(requireContext(), "Service", "DELETED", (int) serviceId);
|
||||
Toast.makeText(getContext(), "Service deleted successfully!", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
@@ -143,14 +148,11 @@ public class ServiceDetailFragment extends Fragment {
|
||||
if (getArguments() != null && getArguments().containsKey("serviceId")) {
|
||||
// Get service data from arguments and populate fields
|
||||
isEditing = true;
|
||||
serviceId = getArguments().getInt("serviceId");
|
||||
serviceId = getArguments().getLong("serviceId");
|
||||
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);
|
||||
loadServiceData();
|
||||
} else {
|
||||
// Service is being added
|
||||
// Set default values for add a new service
|
||||
@@ -161,4 +163,22 @@ public class ServiceDetailFragment extends Fragment {
|
||||
binding.btnSaveService.setText("Add");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches specific service details from the backend using the ID.
|
||||
*/
|
||||
private void loadServiceData() {
|
||||
viewModel.getServiceById(serviceId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
ServiceDTO s = resource.data;
|
||||
binding.etServiceName.setText(s.getServiceName());
|
||||
binding.etServiceDesc.setText(s.getServiceDesc());
|
||||
binding.etServiceDuration.setText(String.valueOf(s.getServiceDuration()));
|
||||
binding.etServicePrice.setText(String.valueOf(s.getServicePrice()));
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Failed to load service: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||
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;
|
||||
@@ -30,7 +31,7 @@ import dagger.hilt.android.AndroidEntryPoint;
|
||||
public class SupplierDetailFragment extends Fragment {
|
||||
|
||||
private FragmentSupplierDetailBinding binding;
|
||||
private int supId;
|
||||
private long supId;
|
||||
private boolean isEditing = false;
|
||||
|
||||
private SupplierViewModel viewModel;
|
||||
@@ -45,6 +46,12 @@ public class SupplierDetailFragment extends Fragment {
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
binding = FragmentSupplierDetailBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
// Add phone number formatting (CA) and limit length to 14 characters
|
||||
UIUtils.formatPhoneInput(binding.etSupPhone);
|
||||
@@ -55,8 +62,6 @@ public class SupplierDetailFragment extends Fragment {
|
||||
binding.btnBack.setOnClickListener(v -> navigateBack());
|
||||
binding.btnSaveSupplier.setOnClickListener(v -> saveSupplier());
|
||||
binding.btnDeleteSupplier.setOnClickListener(v -> deleteSupplier());
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -94,10 +99,10 @@ public class SupplierDetailFragment extends Fragment {
|
||||
//check if the supplier is being edited or added
|
||||
if (isEditing) {
|
||||
// Update existing supplier
|
||||
supplierDTO.setSupId((long) supId);
|
||||
viewModel.updateSupplier((long) supId, supplierDTO).observe(getViewLifecycleOwner(), resource -> {
|
||||
supplierDTO.setSupId(supId);
|
||||
viewModel.updateSupplier(supId, supplierDTO).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
ActivityLogger.logChange(requireContext(), "Supplier", "UPDATED", supId);
|
||||
ActivityLogger.logChange(requireContext(), "Supplier", "UPDATED", (int) supId);
|
||||
Toast.makeText(getContext(), "Supplier updated successfully!", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
@@ -123,9 +128,9 @@ public class SupplierDetailFragment extends Fragment {
|
||||
*/
|
||||
private void deleteSupplier() {
|
||||
DialogUtils.showDeleteConfirmDialog(requireContext(), "Supplier", () ->
|
||||
viewModel.deleteSupplier((long) supId).observe(getViewLifecycleOwner(), resource -> {
|
||||
viewModel.deleteSupplier(supId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
ActivityLogger.logChange(requireContext(), "Supplier", "DELETED", supId);
|
||||
ActivityLogger.logChange(requireContext(), "Supplier", "DELETED", (int) supId);
|
||||
Toast.makeText(getContext(), "Supplier deleted successfully!", Toast.LENGTH_SHORT).show();
|
||||
navigateBack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
@@ -149,15 +154,12 @@ public class SupplierDetailFragment extends Fragment {
|
||||
if (getArguments() != null && getArguments().containsKey("supId")) {
|
||||
// Get supplier data from arguments and populate fields
|
||||
isEditing = true;
|
||||
supId = getArguments().getInt("supId");
|
||||
supId = getArguments().getLong("supId");
|
||||
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.tvSupId.setVisibility(View.VISIBLE);
|
||||
binding.btnDeleteSupplier.setVisibility(View.VISIBLE);
|
||||
loadSupplierData();
|
||||
} else {
|
||||
// Supplier is being added
|
||||
// Set default values for add a new supplier
|
||||
@@ -168,4 +170,23 @@ public class SupplierDetailFragment extends Fragment {
|
||||
binding.btnSaveSupplier.setText("Add");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches specific supplier details from the backend using the ID.
|
||||
*/
|
||||
private void loadSupplierData() {
|
||||
viewModel.getSupplierById(supId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
SupplierDTO s = resource.data;
|
||||
binding.etSupCompany.setText(s.getSupCompany());
|
||||
binding.etSupContactFirstName.setText(s.getSupContactFirstName());
|
||||
binding.etSupContactLastName.setText(s.getSupContactLastName());
|
||||
binding.etSupEmail.setText(s.getSupEmail());
|
||||
binding.etSupPhone.setText(s.getSupPhone());
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Failed to load supplier: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,24 +3,27 @@ package com.example.petstoremobile.fragments.listfragments.listprofilefragments;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.api.PetApi;
|
||||
import com.example.petstoremobile.api.auth.TokenManager;
|
||||
import com.example.petstoremobile.databinding.FragmentPetProfileBinding;
|
||||
import com.example.petstoremobile.dtos.PetDTO;
|
||||
import com.example.petstoremobile.utils.FileUtils;
|
||||
import com.example.petstoremobile.utils.GlideUtils;
|
||||
import com.example.petstoremobile.utils.ImagePickerHelper;
|
||||
import com.example.petstoremobile.utils.RetrofitUtils;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.viewmodels.PetViewModel;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Locale;
|
||||
@@ -36,16 +39,14 @@ import okhttp3.RequestBody;
|
||||
@AndroidEntryPoint
|
||||
public class PetProfileFragment extends Fragment {
|
||||
|
||||
private TextView tvPetName, tvPetSpecies, tvPetBreed, tvPetAge, tvPetPrice;
|
||||
private Button btnBack, btnEditPet, btnChangePhoto;
|
||||
private ImageView imgPet;
|
||||
private int petId;
|
||||
private FragmentPetProfileBinding binding;
|
||||
private long petId;
|
||||
private boolean hasImage = false;
|
||||
|
||||
@Inject PetApi petApi;
|
||||
@Inject @Named("baseUrl") String baseUrl;
|
||||
@Inject TokenManager tokenManager;
|
||||
|
||||
private PetViewModel viewModel;
|
||||
private ImagePickerHelper imagePickerHelper;
|
||||
|
||||
|
||||
@@ -55,6 +56,7 @@ public class PetProfileFragment extends Fragment {
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(PetViewModel.class);
|
||||
|
||||
imagePickerHelper = new ImagePickerHelper(this, "pet_photo.jpg", new ImagePickerHelper.ImagePickerListener() {
|
||||
@Override
|
||||
@@ -70,65 +72,77 @@ public class PetProfileFragment extends Fragment {
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout, initializes views, and sets up click listeners.
|
||||
* Inflates the layout using view binding, initializes views, and sets up click listeners.
|
||||
*/
|
||||
@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_profile, container, false);
|
||||
|
||||
// Initialize views
|
||||
tvPetName = view.findViewById(R.id.tvPetName);
|
||||
tvPetSpecies = view.findViewById(R.id.tvPetSpecies);
|
||||
tvPetBreed = view.findViewById(R.id.tvPetBreed);
|
||||
tvPetAge = view.findViewById(R.id.tvPetAge);
|
||||
tvPetPrice = view.findViewById(R.id.tvPetPrice);
|
||||
btnBack = view.findViewById(R.id.btnBack);
|
||||
btnEditPet = view.findViewById(R.id.btnEditPet);
|
||||
btnChangePhoto = view.findViewById(R.id.btnChangePhoto);
|
||||
imgPet = view.findViewById(R.id.imgPet);
|
||||
|
||||
binding = FragmentPetProfileBinding.inflate(inflater, container, false);
|
||||
|
||||
// Set pet details to display
|
||||
if (getArguments() != null) {
|
||||
petId = getArguments().getInt("petId");
|
||||
tvPetName.setText(getArguments().getString("petName"));
|
||||
tvPetSpecies.setText(getArguments().getString("petSpecies"));
|
||||
tvPetBreed.setText(getArguments().getString("petBreed"));
|
||||
tvPetAge.setText(String.format(Locale.getDefault(), "%d yr(s)", getArguments().getInt("petAge")));
|
||||
tvPetPrice.setText(String.format(Locale.getDefault(), "$%.2f", getArguments().getDouble("petPrice")));
|
||||
|
||||
// Load pet image from backend
|
||||
loadPetImage(petId);
|
||||
petId = getArguments().getLong("petId");
|
||||
loadPetData();
|
||||
loadPetImage((int) petId);
|
||||
}
|
||||
|
||||
//set button click listeners
|
||||
btnBack.setOnClickListener(v -> {
|
||||
binding.btnBack.setOnClickListener(v -> {
|
||||
NavHostFragment.findNavController(this).popBackStack();
|
||||
});
|
||||
|
||||
//Make the edit button go to the pet detail view
|
||||
btnEditPet.setOnClickListener(v -> {
|
||||
if (getArguments() == null) return;
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_pet_detail, getArguments());
|
||||
binding.btnEditPet.setOnClickListener(v -> {
|
||||
Bundle args = new Bundle();
|
||||
args.putLong("petId", petId);
|
||||
NavHostFragment.findNavController(this).navigate(R.id.nav_pet_detail, args);
|
||||
});
|
||||
|
||||
//Make change photo button ask user to select a new photo
|
||||
btnChangePhoto.setOnClickListener(v -> {
|
||||
binding.btnChangePhoto.setOnClickListener(v -> {
|
||||
imagePickerHelper.showImagePickerDialog("Change Pet Photo", hasImage);
|
||||
});
|
||||
|
||||
return view;
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches and displays the pet's image from the server.
|
||||
* Fetches current pet data from the backend and updates the UI.
|
||||
*/
|
||||
private void loadPetData() {
|
||||
viewModel.getPetById(petId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
PetDTO pet = resource.data;
|
||||
binding.tvPetName.setText(pet.getPetName());
|
||||
binding.tvPetSpecies.setText(pet.getPetSpecies());
|
||||
binding.tvPetBreed.setText(pet.getPetBreed());
|
||||
binding.tvPetAge.setText(String.format(Locale.getDefault(), "%d yr(s)", pet.getPetAge()));
|
||||
try {
|
||||
binding.tvPetPrice.setText(String.format(Locale.getDefault(), "$%.2f", Double.parseDouble(pet.getPetPrice())));
|
||||
} catch (Exception e) {
|
||||
binding.tvPetPrice.setText("$0.00");
|
||||
}
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Failed to load pet data: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches and displays the pet\'s image from the server.
|
||||
*/
|
||||
private void loadPetImage(int petId) {
|
||||
String imageUrl = baseUrl + String.format(Locale.US, PetApi.PET_IMAGE_PATH, petId);
|
||||
String token = tokenManager.getToken();
|
||||
|
||||
GlideUtils.loadImageWithToken(requireContext(), imgPet, imageUrl, token, R.drawable.placeholder, new GlideUtils.ImageLoadListener() {
|
||||
GlideUtils.loadImageWithToken(requireContext(), binding.imgPet, imageUrl, token, R.drawable.placeholder, new GlideUtils.ImageLoadListener() {
|
||||
@Override
|
||||
public void onResourceReady() {
|
||||
hasImage = true;
|
||||
@@ -142,7 +156,7 @@ public class PetProfileFragment extends Fragment {
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads a selected or captured image a pet photo through the API.
|
||||
* Uploads a selected or captured image a pet photo through the ViewModel.
|
||||
*/
|
||||
private void uploadPetImage(Uri uri) {
|
||||
try {
|
||||
@@ -153,30 +167,36 @@ public class PetProfileFragment extends Fragment {
|
||||
RequestBody requestFile = RequestBody.create(file, MediaType.parse(requireContext().getContentResolver().getType(uri)));
|
||||
MultipartBody.Part body = MultipartBody.Part.createFormData("image", file.getName(), requestFile);
|
||||
|
||||
// Call the backend to upload the image
|
||||
petApi.uploadPetImage((long) petId, body).enqueue(RetrofitUtils.createCallback(
|
||||
requireContext(),
|
||||
"UPLOAD_PET_IMAGE",
|
||||
"Pet photo updated successfully",
|
||||
result -> loadPetImage(petId)
|
||||
));
|
||||
// Use ViewModel to upload image
|
||||
viewModel.uploadPetImage(petId, body).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status != Resource.Status.LOADING) {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), "Pet photo updated successfully", Toast.LENGTH_SHORT).show();
|
||||
loadPetImage((int) petId);
|
||||
} else {
|
||||
Toast.makeText(getContext(), "Upload failed: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Log.e("UPLOAD_PET_IMAGE", "Error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to remove the current pet photo.
|
||||
* Sends a request to the ViewModel to remove the current pet photo.
|
||||
*/
|
||||
private void deletePetImage() {
|
||||
petApi.deletePetImage((long) petId).enqueue(RetrofitUtils.createCallback(
|
||||
requireContext(),
|
||||
"DELETE_PET_IMAGE",
|
||||
"Pet photo removed",
|
||||
result -> {
|
||||
viewModel.deletePetImage(petId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource != null && resource.status != Resource.Status.LOADING) {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), "Pet photo removed", Toast.LENGTH_SHORT).show();
|
||||
hasImage = false;
|
||||
imgPet.setImageResource(R.drawable.placeholder);
|
||||
binding.imgPet.setImageResource(R.drawable.placeholder);
|
||||
} else {
|
||||
Toast.makeText(getContext(), "Delete failed: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
package com.example.petstoremobile.repositories;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.example.petstoremobile.api.AdoptionApi;
|
||||
import com.example.petstoremobile.dtos.AdoptionDTO;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.RetrofitUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class AdoptionRepository {
|
||||
private static final String TAG = "AdoptionRepository";
|
||||
public class AdoptionRepository extends BaseRepository {
|
||||
private final AdoptionApi adoptionApi;
|
||||
|
||||
@Inject
|
||||
public AdoptionRepository(AdoptionApi adoptionApi) {
|
||||
super("AdoptionRepository");
|
||||
this.adoptionApi = adoptionApi;
|
||||
}
|
||||
|
||||
@@ -26,44 +24,34 @@ public class AdoptionRepository {
|
||||
* Retrieves a paginated list of all adoptions from the API.
|
||||
*/
|
||||
public LiveData<Resource<PageResponse<AdoptionDTO>>> getAllAdoptions(int page, int size) {
|
||||
MutableLiveData<Resource<PageResponse<AdoptionDTO>>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(adoptionApi.getAllAdoptions(page, size), data, TAG);
|
||||
return data;
|
||||
return executeCall(adoptionApi.getAllAdoptions(page, size));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a specific adoption record by its ID from the API.
|
||||
*/
|
||||
public LiveData<Resource<AdoptionDTO>> getAdoptionById(Long id) {
|
||||
MutableLiveData<Resource<AdoptionDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(adoptionApi.getAdoptionById(id), data, TAG);
|
||||
return data;
|
||||
return executeCall(adoptionApi.getAdoptionById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to create a new adoption record.
|
||||
*/
|
||||
public LiveData<Resource<AdoptionDTO>> createAdoption(AdoptionDTO adoption) {
|
||||
MutableLiveData<Resource<AdoptionDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(adoptionApi.createAdoption(adoption), data, TAG);
|
||||
return data;
|
||||
return executeCall(adoptionApi.createAdoption(adoption));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to update an existing adoption record by ID.
|
||||
*/
|
||||
public LiveData<Resource<AdoptionDTO>> updateAdoption(Long id, AdoptionDTO adoption) {
|
||||
MutableLiveData<Resource<AdoptionDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(adoptionApi.updateAdoption(id, adoption), data, TAG);
|
||||
return data;
|
||||
return executeCall(adoptionApi.updateAdoption(id, adoption));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to delete a specific adoption record.
|
||||
*/
|
||||
public LiveData<Resource<Void>> deleteAdoption(Long id) {
|
||||
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(adoptionApi.deleteAdoption(id), data, TAG);
|
||||
return data;
|
||||
return executeCall(adoptionApi.deleteAdoption(id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
package com.example.petstoremobile.repositories;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.example.petstoremobile.api.AppointmentApi;
|
||||
import com.example.petstoremobile.dtos.AppointmentDTO;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.RetrofitUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class AppointmentRepository {
|
||||
private static final String TAG = "AppointmentRepository";
|
||||
public class AppointmentRepository extends BaseRepository {
|
||||
private final AppointmentApi appointmentApi;
|
||||
|
||||
@Inject
|
||||
public AppointmentRepository(AppointmentApi appointmentApi) {
|
||||
super("AppointmentRepository");
|
||||
this.appointmentApi = appointmentApi;
|
||||
}
|
||||
|
||||
@@ -26,44 +24,34 @@ public class AppointmentRepository {
|
||||
* Retrieves a paginated list of all appointments from the API.
|
||||
*/
|
||||
public LiveData<Resource<PageResponse<AppointmentDTO>>> getAllAppointments(int page, int size) {
|
||||
MutableLiveData<Resource<PageResponse<AppointmentDTO>>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(appointmentApi.getAllAppointments(page, size), data, TAG);
|
||||
return data;
|
||||
return executeCall(appointmentApi.getAllAppointments(page, size));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a specific appointment by its ID from the API.
|
||||
*/
|
||||
public LiveData<Resource<AppointmentDTO>> getAppointmentById(Long id) {
|
||||
MutableLiveData<Resource<AppointmentDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(appointmentApi.getAppointmentById(id), data, TAG);
|
||||
return data;
|
||||
return executeCall(appointmentApi.getAppointmentById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to create a new appointment record.
|
||||
*/
|
||||
public LiveData<Resource<AppointmentDTO>> createAppointment(AppointmentDTO appointment) {
|
||||
MutableLiveData<Resource<AppointmentDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(appointmentApi.createAppointment(appointment), data, TAG);
|
||||
return data;
|
||||
return executeCall(appointmentApi.createAppointment(appointment));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to update an existing appointment record by ID.
|
||||
*/
|
||||
public LiveData<Resource<AppointmentDTO>> updateAppointment(Long id, AppointmentDTO appointment) {
|
||||
MutableLiveData<Resource<AppointmentDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(appointmentApi.updateAppointment(id, appointment), data, TAG);
|
||||
return data;
|
||||
return executeCall(appointmentApi.updateAppointment(id, appointment));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to delete a specific appointment record.
|
||||
*/
|
||||
public LiveData<Resource<Void>> deleteAppointment(Long id) {
|
||||
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(appointmentApi.deleteAppointment(id), data, TAG);
|
||||
return data;
|
||||
return executeCall(appointmentApi.deleteAppointment(id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.example.petstoremobile.repositories;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
@@ -12,7 +10,6 @@ import com.example.petstoremobile.dtos.AuthDTO;
|
||||
import com.example.petstoremobile.dtos.UserDTO;
|
||||
import com.example.petstoremobile.utils.ErrorUtils;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.RetrofitUtils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -25,13 +22,13 @@ import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
@Singleton
|
||||
public class AuthRepository {
|
||||
private static final String TAG = "AuthRepository";
|
||||
public class AuthRepository extends BaseRepository {
|
||||
private final AuthApi authApi;
|
||||
private final TokenManager tokenManager;
|
||||
|
||||
@Inject
|
||||
public AuthRepository(AuthApi authApi, TokenManager tokenManager) {
|
||||
super("AuthRepository");
|
||||
this.authApi = authApi;
|
||||
this.tokenManager = tokenManager;
|
||||
}
|
||||
@@ -62,7 +59,7 @@ public class AuthRepository {
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<AuthDTO.LoginResponse> call, @NonNull Throwable t) {
|
||||
data.setValue(Resource.error("Network error: " + t.getMessage(), null));
|
||||
data.setValue(Resource.error(ErrorUtils.getFailureMessage(t), null));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -73,36 +70,28 @@ public class AuthRepository {
|
||||
* Retrieves the current user's profile information from the API.
|
||||
*/
|
||||
public LiveData<Resource<UserDTO>> getMe() {
|
||||
MutableLiveData<Resource<UserDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(authApi.getMe(), data, TAG);
|
||||
return data;
|
||||
return executeCall(authApi.getMe());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current user's profile details.
|
||||
*/
|
||||
public LiveData<Resource<UserDTO>> updateMe(Map<String, String> updates) {
|
||||
MutableLiveData<Resource<UserDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(authApi.updateMe(updates), data, TAG);
|
||||
return data;
|
||||
return executeCall(authApi.updateMe(updates));
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads a multipart image to be used as the current user's avatar.
|
||||
*/
|
||||
public LiveData<Resource<UserDTO>> uploadAvatar(MultipartBody.Part avatar) {
|
||||
MutableLiveData<Resource<UserDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(authApi.uploadAvatar(avatar), data, TAG);
|
||||
return data;
|
||||
return executeCall(authApi.uploadAvatar(avatar));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to remove the current user's avatar.
|
||||
*/
|
||||
public LiveData<Resource<Void>> deleteAvatar() {
|
||||
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(authApi.deleteAvatar(), data, TAG);
|
||||
return data;
|
||||
return executeCall(authApi.deleteAvatar());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.example.petstoremobile.repositories;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.RetrofitUtils;
|
||||
|
||||
import retrofit2.Call;
|
||||
|
||||
/**
|
||||
* Base class for all repositories to provide common functionality for API calls.
|
||||
*/
|
||||
public abstract class BaseRepository {
|
||||
protected final String TAG;
|
||||
|
||||
protected BaseRepository(String tag) {
|
||||
this.TAG = tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a Retrofit call and returns a LiveData containing the Resource.
|
||||
*/
|
||||
protected <T> LiveData<Resource<T>> executeCall(Call<T> call) {
|
||||
MutableLiveData<Resource<T>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(call, data, TAG);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,22 @@
|
||||
package com.example.petstoremobile.repositories;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.example.petstoremobile.api.CategoryApi;
|
||||
import com.example.petstoremobile.dtos.CategoryDTO;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.RetrofitUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class CategoryRepository {
|
||||
private static final String TAG = "CategoryRepository";
|
||||
public class CategoryRepository extends BaseRepository {
|
||||
private final CategoryApi categoryApi;
|
||||
|
||||
@Inject
|
||||
public CategoryRepository(CategoryApi categoryApi) {
|
||||
super("CategoryRepository");
|
||||
this.categoryApi = categoryApi;
|
||||
}
|
||||
|
||||
@@ -26,8 +24,6 @@ public class CategoryRepository {
|
||||
* Retrieves a paginated list of all product categories from the API.
|
||||
*/
|
||||
public LiveData<Resource<PageResponse<CategoryDTO>>> getAllCategories(int page, int size) {
|
||||
MutableLiveData<Resource<PageResponse<CategoryDTO>>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(categoryApi.getAllCategories(page, size), data, TAG);
|
||||
return data;
|
||||
return executeCall(categoryApi.getAllCategories(page, size));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.example.petstoremobile.repositories;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
|
||||
import com.example.petstoremobile.api.ChatApi;
|
||||
import com.example.petstoremobile.api.CustomerApi;
|
||||
import com.example.petstoremobile.api.MessageApi;
|
||||
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.utils.Resource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import okhttp3.MultipartBody;
|
||||
import okhttp3.RequestBody;
|
||||
|
||||
/**
|
||||
* Repository for handling chat-related data operations.
|
||||
*/
|
||||
@Singleton
|
||||
public class ChatRepository extends BaseRepository {
|
||||
private final ChatApi chatApi;
|
||||
private final MessageApi messageApi;
|
||||
private final CustomerApi customerApi;
|
||||
|
||||
@Inject
|
||||
public ChatRepository(ChatApi chatApi, MessageApi messageApi, CustomerApi customerApi) {
|
||||
super("ChatRepository");
|
||||
this.chatApi = chatApi;
|
||||
this.messageApi = messageApi;
|
||||
this.customerApi = customerApi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all chat conversations for the current user.
|
||||
*/
|
||||
public LiveData<Resource<List<ConversationDTO>>> getAllConversations() {
|
||||
return executeCall(chatApi.getAllConversations());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the message history for a specific conversation.
|
||||
*/
|
||||
public LiveData<Resource<List<MessageDTO>>> getMessages(Long conversationId) {
|
||||
return executeCall(messageApi.getMessages(conversationId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a plain text message to a conversation.
|
||||
*/
|
||||
public LiveData<Resource<MessageDTO>> sendMessage(Long conversationId, SendMessageRequest request) {
|
||||
return executeCall(messageApi.sendMessage(conversationId, request));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message with a file attachment to a conversation.
|
||||
*/
|
||||
public LiveData<Resource<MessageDTO>> sendMessageWithAttachment(Long conversationId, RequestBody content, MultipartBody.Part file) {
|
||||
return executeCall(messageApi.sendMessageWithAttachment(conversationId, content, file));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a paginated list of customers.
|
||||
*/
|
||||
public LiveData<Resource<PageResponse<CustomerDTO>>> getAllCustomers(int page, int size) {
|
||||
return executeCall(customerApi.getAllCustomers(page, size));
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,22 @@
|
||||
package com.example.petstoremobile.repositories;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.example.petstoremobile.api.CustomerApi;
|
||||
import com.example.petstoremobile.dtos.CustomerDTO;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.RetrofitUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class CustomerRepository {
|
||||
private static final String TAG = "CustomerRepository";
|
||||
public class CustomerRepository extends BaseRepository {
|
||||
private final CustomerApi customerApi;
|
||||
|
||||
@Inject
|
||||
public CustomerRepository(CustomerApi customerApi) {
|
||||
super("CustomerRepository");
|
||||
this.customerApi = customerApi;
|
||||
}
|
||||
|
||||
@@ -26,17 +24,13 @@ public class CustomerRepository {
|
||||
* Retrieves a paginated list of all customers from the API.
|
||||
*/
|
||||
public LiveData<Resource<PageResponse<CustomerDTO>>> getAllCustomers(int page, int size) {
|
||||
MutableLiveData<Resource<PageResponse<CustomerDTO>>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(customerApi.getAllCustomers(page, size), data, TAG);
|
||||
return data;
|
||||
return executeCall(customerApi.getAllCustomers(page, size));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a specific customer by their ID.
|
||||
*/
|
||||
public LiveData<Resource<CustomerDTO>> getCustomerById(Long id) {
|
||||
MutableLiveData<Resource<CustomerDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(customerApi.getCustomerById(id), data, TAG);
|
||||
return data;
|
||||
return executeCall(customerApi.getCustomerById(id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.example.petstoremobile.repositories;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.example.petstoremobile.api.InventoryApi;
|
||||
import com.example.petstoremobile.dtos.BulkDeleteRequest;
|
||||
@@ -9,20 +8,17 @@ import com.example.petstoremobile.dtos.InventoryDTO;
|
||||
import com.example.petstoremobile.dtos.InventoryRequest;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.RetrofitUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class InventoryRepository {
|
||||
private static final String TAG = "InventoryRepository";
|
||||
public class InventoryRepository extends BaseRepository {
|
||||
private final InventoryApi inventoryApi;
|
||||
|
||||
@Inject
|
||||
public InventoryRepository(InventoryApi inventoryApi) {
|
||||
super("InventoryRepository");
|
||||
this.inventoryApi = inventoryApi;
|
||||
}
|
||||
|
||||
@@ -30,47 +26,35 @@ public class InventoryRepository {
|
||||
* Retrieves a paginated list of inventory items from the API with optional search and sort.
|
||||
*/
|
||||
public LiveData<Resource<PageResponse<InventoryDTO>>> getAllInventory(String query, int page, int size, String sort) {
|
||||
MutableLiveData<Resource<PageResponse<InventoryDTO>>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(inventoryApi.getAllInventory(query, page, size, sort), data, TAG);
|
||||
return data;
|
||||
return executeCall(inventoryApi.getAllInventory(query, page, size, sort));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a specific inventory item by its ID from the API.
|
||||
*/
|
||||
public LiveData<Resource<InventoryDTO>> getInventoryById(Long id) {
|
||||
MutableLiveData<Resource<InventoryDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(inventoryApi.getInventoryById(id), data, TAG);
|
||||
return data;
|
||||
return executeCall(inventoryApi.getInventoryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to create a new inventory record.
|
||||
*/
|
||||
public LiveData<Resource<InventoryDTO>> createInventory(InventoryRequest request) {
|
||||
MutableLiveData<Resource<InventoryDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(inventoryApi.createInventory(request), data, TAG);
|
||||
return data;
|
||||
return executeCall(inventoryApi.createInventory(request));
|
||||
}
|
||||
|
||||
public LiveData<Resource<InventoryDTO>> updateInventory(Long id, InventoryRequest request) {
|
||||
MutableLiveData<Resource<InventoryDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(inventoryApi.updateInventory(id, request), data, TAG);
|
||||
return data;
|
||||
return executeCall(inventoryApi.updateInventory(id, request));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to delete a specific inventory record.
|
||||
*/
|
||||
public LiveData<Resource<Void>> deleteInventory(Long id) {
|
||||
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(inventoryApi.deleteInventory(id), data, TAG);
|
||||
return data;
|
||||
return executeCall(inventoryApi.deleteInventory(id));
|
||||
}
|
||||
|
||||
public LiveData<Resource<Void>> bulkDeleteInventory(BulkDeleteRequest request) {
|
||||
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(inventoryApi.bulkDeleteInventory(request), data, TAG);
|
||||
return data;
|
||||
return executeCall(inventoryApi.bulkDeleteInventory(request));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
package com.example.petstoremobile.repositories;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.example.petstoremobile.api.PetApi;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.dtos.PetDTO;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.RetrofitUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
@@ -15,12 +13,12 @@ import javax.inject.Singleton;
|
||||
import okhttp3.MultipartBody;
|
||||
|
||||
@Singleton
|
||||
public class PetRepository {
|
||||
private static final String TAG = "PetRepository";
|
||||
public class PetRepository extends BaseRepository {
|
||||
private final PetApi petApi;
|
||||
|
||||
@Inject
|
||||
public PetRepository(PetApi petApi) {
|
||||
super("PetRepository");
|
||||
this.petApi = petApi;
|
||||
}
|
||||
|
||||
@@ -28,62 +26,48 @@ public class PetRepository {
|
||||
* Retrieves a paginated list of all pets from the API.
|
||||
*/
|
||||
public LiveData<Resource<PageResponse<PetDTO>>> getAllPets(int page, int size) {
|
||||
MutableLiveData<Resource<PageResponse<PetDTO>>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(petApi.getAllPets(page, size), data, TAG);
|
||||
return data;
|
||||
return executeCall(petApi.getAllPets(page, size));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a specific pet by its ID from the API.
|
||||
*/
|
||||
public LiveData<Resource<PetDTO>> getPetById(Long id) {
|
||||
MutableLiveData<Resource<PetDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(petApi.getPetById(id), data, TAG);
|
||||
return data;
|
||||
return executeCall(petApi.getPetById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to create a new pet record.
|
||||
*/
|
||||
public LiveData<Resource<PetDTO>> createPet(PetDTO pet) {
|
||||
MutableLiveData<Resource<PetDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(petApi.createPet(pet), data, TAG);
|
||||
return data;
|
||||
return executeCall(petApi.createPet(pet));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to update an existing pet record.
|
||||
*/
|
||||
public LiveData<Resource<PetDTO>> updatePet(Long id, PetDTO pet) {
|
||||
MutableLiveData<Resource<PetDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(petApi.updatePet(id, pet), data, TAG);
|
||||
return data;
|
||||
return executeCall(petApi.updatePet(id, pet));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to delete a specific pet record.
|
||||
*/
|
||||
public LiveData<Resource<Void>> deletePet(Long id) {
|
||||
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(petApi.deletePet(id), data, TAG);
|
||||
return data;
|
||||
return executeCall(petApi.deletePet(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads an image file for a specific pet via the API.
|
||||
*/
|
||||
public LiveData<Resource<Void>> uploadPetImage(Long id, MultipartBody.Part image) {
|
||||
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(petApi.uploadPetImage(id, image), data, TAG);
|
||||
return data;
|
||||
return executeCall(petApi.uploadPetImage(id, image));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to delete the image of a specific pet.
|
||||
*/
|
||||
public LiveData<Resource<Void>> deletePetImage(Long id) {
|
||||
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(petApi.deletePetImage(id), data, TAG);
|
||||
return data;
|
||||
return executeCall(petApi.deletePetImage(id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
package com.example.petstoremobile.repositories;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.example.petstoremobile.api.ProductApi;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.dtos.ProductDTO;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.RetrofitUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
@@ -15,12 +13,12 @@ import javax.inject.Singleton;
|
||||
import okhttp3.MultipartBody;
|
||||
|
||||
@Singleton
|
||||
public class ProductRepository {
|
||||
private static final String TAG = "ProductRepository";
|
||||
public class ProductRepository extends BaseRepository {
|
||||
private final ProductApi productApi;
|
||||
|
||||
@Inject
|
||||
public ProductRepository(ProductApi productApi) {
|
||||
super("ProductRepository");
|
||||
this.productApi = productApi;
|
||||
}
|
||||
|
||||
@@ -28,62 +26,48 @@ public class ProductRepository {
|
||||
* Retrieves a paginated list of products from the API, filtered by an optional query.
|
||||
*/
|
||||
public LiveData<Resource<PageResponse<ProductDTO>>> getAllProducts(String query, int page, int size) {
|
||||
MutableLiveData<Resource<PageResponse<ProductDTO>>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(productApi.getAllProducts(query, page, size), data, TAG);
|
||||
return data;
|
||||
return executeCall(productApi.getAllProducts(query, page, size));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a specific product by its ID from the API.
|
||||
*/
|
||||
public LiveData<Resource<ProductDTO>> getProductById(Long id) {
|
||||
MutableLiveData<Resource<ProductDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(productApi.getProductById(id), data, TAG);
|
||||
return data;
|
||||
return executeCall(productApi.getProductById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to create a new product.
|
||||
*/
|
||||
public LiveData<Resource<ProductDTO>> createProduct(ProductDTO product) {
|
||||
MutableLiveData<Resource<ProductDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(productApi.createProduct(product), data, TAG);
|
||||
return data;
|
||||
return executeCall(productApi.createProduct(product));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to update an existing product by ID.
|
||||
*/
|
||||
public LiveData<Resource<ProductDTO>> updateProduct(Long id, ProductDTO product) {
|
||||
MutableLiveData<Resource<ProductDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(productApi.updateProduct(id, product), data, TAG);
|
||||
return data;
|
||||
return executeCall(productApi.updateProduct(id, product));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to delete a specific product.
|
||||
*/
|
||||
public LiveData<Resource<Void>> deleteProduct(Long id) {
|
||||
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(productApi.deleteProduct(id), data, TAG);
|
||||
return data;
|
||||
return executeCall(productApi.deleteProduct(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads an image file for a specific product via the API.
|
||||
*/
|
||||
public LiveData<Resource<Void>> uploadProductImage(Long id, MultipartBody.Part image) {
|
||||
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(productApi.uploadProductImage(id, image), data, TAG);
|
||||
return data;
|
||||
return executeCall(productApi.uploadProductImage(id, image));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to delete the image of a specific product.
|
||||
*/
|
||||
public LiveData<Resource<Void>> deleteProductImage(Long id) {
|
||||
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(productApi.deleteProductImage(id), data, TAG);
|
||||
return data;
|
||||
return executeCall(productApi.deleteProductImage(id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
package com.example.petstoremobile.repositories;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.example.petstoremobile.api.ProductSupplierApi;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.dtos.ProductSupplierDTO;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.RetrofitUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class ProductSupplierRepository {
|
||||
private static final String TAG = "ProductSupplierRepository";
|
||||
public class ProductSupplierRepository extends BaseRepository {
|
||||
private final ProductSupplierApi api;
|
||||
|
||||
@Inject
|
||||
public ProductSupplierRepository(ProductSupplierApi api) {
|
||||
super("ProductSupplierRepository");
|
||||
this.api = api;
|
||||
}
|
||||
|
||||
@@ -26,35 +24,34 @@ public class ProductSupplierRepository {
|
||||
* Retrieves a paginated list of all product-supplier relationships from the API.
|
||||
*/
|
||||
public LiveData<Resource<PageResponse<ProductSupplierDTO>>> getAllProductSuppliers(int page, int size) {
|
||||
MutableLiveData<Resource<PageResponse<ProductSupplierDTO>>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(api.getAllProductSuppliers(page, size), data, TAG);
|
||||
return data;
|
||||
return executeCall(api.getAllProductSuppliers(page, size));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a single product-supplier relationship by product and supplier IDs.
|
||||
*/
|
||||
public LiveData<Resource<ProductSupplierDTO>> getProductSupplierById(Long productId, Long supplierId) {
|
||||
return executeCall(api.getProductSupplierById(productId, supplierId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to create a new product-supplier relationship.
|
||||
*/
|
||||
public LiveData<Resource<ProductSupplierDTO>> createProductSupplier(ProductSupplierDTO dto) {
|
||||
MutableLiveData<Resource<ProductSupplierDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(api.createProductSupplier(dto), data, TAG);
|
||||
return data;
|
||||
return executeCall(api.createProductSupplier(dto));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to update an existing product-supplier relationship.
|
||||
*/
|
||||
public LiveData<Resource<ProductSupplierDTO>> updateProductSupplier(Long productId, Long supplierId, ProductSupplierDTO dto) {
|
||||
MutableLiveData<Resource<ProductSupplierDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(api.updateProductSupplier(productId, supplierId, dto), data, TAG);
|
||||
return data;
|
||||
return executeCall(api.updateProductSupplier(productId, supplierId, dto));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to delete a specific product-supplier relationship.
|
||||
*/
|
||||
public LiveData<Resource<Void>> deleteProductSupplier(Long productId, Long supplierId) {
|
||||
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(api.deleteProductSupplier(productId, supplierId), data, TAG);
|
||||
return data;
|
||||
return executeCall(api.deleteProductSupplier(productId, supplierId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
package com.example.petstoremobile.repositories;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.example.petstoremobile.api.PurchaseOrderApi;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.dtos.PurchaseOrderDTO;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.RetrofitUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class PurchaseOrderRepository {
|
||||
private static final String TAG = "PurchaseOrderRepo";
|
||||
public class PurchaseOrderRepository extends BaseRepository {
|
||||
private final PurchaseOrderApi api;
|
||||
|
||||
@Inject
|
||||
public PurchaseOrderRepository(PurchaseOrderApi api) {
|
||||
super("PurchaseOrderRepo");
|
||||
this.api = api;
|
||||
}
|
||||
|
||||
@@ -26,17 +24,13 @@ public class PurchaseOrderRepository {
|
||||
* Retrieves a paginated list of all purchase orders from the API.
|
||||
*/
|
||||
public LiveData<Resource<PageResponse<PurchaseOrderDTO>>> getAllPurchaseOrders(int page, int size) {
|
||||
MutableLiveData<Resource<PageResponse<PurchaseOrderDTO>>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(api.getAllPurchaseOrders(page, size), data, TAG);
|
||||
return data;
|
||||
return executeCall(api.getAllPurchaseOrders(page, size));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a specific purchase order by its ID from the API.
|
||||
*/
|
||||
public LiveData<Resource<PurchaseOrderDTO>> getPurchaseOrderById(Long id) {
|
||||
MutableLiveData<Resource<PurchaseOrderDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(api.getPurchaseOrderById(id), data, TAG);
|
||||
return data;
|
||||
return executeCall(api.getPurchaseOrderById(id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
package com.example.petstoremobile.repositories;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.example.petstoremobile.api.ServiceApi;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.dtos.ServiceDTO;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.RetrofitUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class ServiceRepository {
|
||||
private static final String TAG = "ServiceRepository";
|
||||
public class ServiceRepository extends BaseRepository {
|
||||
private final ServiceApi serviceApi;
|
||||
|
||||
@Inject
|
||||
public ServiceRepository(ServiceApi serviceApi) {
|
||||
super("ServiceRepository");
|
||||
this.serviceApi = serviceApi;
|
||||
}
|
||||
|
||||
@@ -26,44 +24,34 @@ public class ServiceRepository {
|
||||
* Retrieves a paginated list of all services from the API.
|
||||
*/
|
||||
public LiveData<Resource<PageResponse<ServiceDTO>>> getAllServices(int page, int size) {
|
||||
MutableLiveData<Resource<PageResponse<ServiceDTO>>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(serviceApi.getAllServices(page, size), data, TAG);
|
||||
return data;
|
||||
return executeCall(serviceApi.getAllServices(page, size));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a specific service by its ID from the API.
|
||||
*/
|
||||
public LiveData<Resource<ServiceDTO>> getServiceById(Long id) {
|
||||
MutableLiveData<Resource<ServiceDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(serviceApi.getServiceById(id), data, TAG);
|
||||
return data;
|
||||
return executeCall(serviceApi.getServiceById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to create a new service.
|
||||
*/
|
||||
public LiveData<Resource<ServiceDTO>> createService(ServiceDTO service) {
|
||||
MutableLiveData<Resource<ServiceDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(serviceApi.createService(service), data, TAG);
|
||||
return data;
|
||||
return executeCall(serviceApi.createService(service));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to update an existing service by ID.
|
||||
*/
|
||||
public LiveData<Resource<ServiceDTO>> updateService(Long id, ServiceDTO service) {
|
||||
MutableLiveData<Resource<ServiceDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(serviceApi.updateService(id, service), data, TAG);
|
||||
return data;
|
||||
return executeCall(serviceApi.updateService(id, service));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to delete a specific service.
|
||||
*/
|
||||
public LiveData<Resource<Void>> deleteService(Long id) {
|
||||
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(serviceApi.deleteService(id), data, TAG);
|
||||
return data;
|
||||
return executeCall(serviceApi.deleteService(id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
package com.example.petstoremobile.repositories;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.example.petstoremobile.api.StoreApi;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.dtos.StoreDTO;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.RetrofitUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class StoreRepository {
|
||||
private static final String TAG = "StoreRepository";
|
||||
public class StoreRepository extends BaseRepository {
|
||||
private final StoreApi storeApi;
|
||||
|
||||
@Inject
|
||||
public StoreRepository(StoreApi storeApi) {
|
||||
super("StoreRepository");
|
||||
this.storeApi = storeApi;
|
||||
}
|
||||
|
||||
@@ -26,8 +24,6 @@ public class StoreRepository {
|
||||
* Retrieves a paginated list of all stores from the API.
|
||||
*/
|
||||
public LiveData<Resource<PageResponse<StoreDTO>>> getAllStores(int page, int size) {
|
||||
MutableLiveData<Resource<PageResponse<StoreDTO>>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(storeApi.getAllStores(page, size), data, TAG);
|
||||
return data;
|
||||
return executeCall(storeApi.getAllStores(page, size));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
package com.example.petstoremobile.repositories;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.example.petstoremobile.api.SupplierApi;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.dtos.SupplierDTO;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.RetrofitUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class SupplierRepository {
|
||||
private static final String TAG = "SupplierRepository";
|
||||
public class SupplierRepository extends BaseRepository {
|
||||
private final SupplierApi supplierApi;
|
||||
|
||||
@Inject
|
||||
public SupplierRepository(SupplierApi supplierApi) {
|
||||
super("SupplierRepository");
|
||||
this.supplierApi = supplierApi;
|
||||
}
|
||||
|
||||
@@ -26,44 +24,34 @@ public class SupplierRepository {
|
||||
* Retrieves a paginated list of all suppliers from the API.
|
||||
*/
|
||||
public LiveData<Resource<PageResponse<SupplierDTO>>> getAllSuppliers(int page, int size) {
|
||||
MutableLiveData<Resource<PageResponse<SupplierDTO>>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(supplierApi.getAllSuppliers(page, size), data, TAG);
|
||||
return data;
|
||||
return executeCall(supplierApi.getAllSuppliers(page, size));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a specific supplier by its ID from the API.
|
||||
*/
|
||||
public LiveData<Resource<SupplierDTO>> getSupplierById(Long id) {
|
||||
MutableLiveData<Resource<SupplierDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(supplierApi.getSupplierById(id), data, TAG);
|
||||
return data;
|
||||
return executeCall(supplierApi.getSupplierById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to create a new supplier record.
|
||||
*/
|
||||
public LiveData<Resource<SupplierDTO>> createSupplier(SupplierDTO supplier) {
|
||||
MutableLiveData<Resource<SupplierDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(supplierApi.createSupplier(supplier), data, TAG);
|
||||
return data;
|
||||
return executeCall(supplierApi.createSupplier(supplier));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to update an existing supplier record by ID.
|
||||
*/
|
||||
public LiveData<Resource<SupplierDTO>> updateSupplier(Long id, SupplierDTO supplier) {
|
||||
MutableLiveData<Resource<SupplierDTO>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(supplierApi.updateSupplier(id, supplier), data, TAG);
|
||||
return data;
|
||||
return executeCall(supplierApi.updateSupplier(id, supplier));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the API to delete a specific supplier record.
|
||||
*/
|
||||
public LiveData<Resource<Void>> deleteSupplier(Long id) {
|
||||
MutableLiveData<Resource<Void>> data = new MutableLiveData<>();
|
||||
RetrofitUtils.enqueue(supplierApi.deleteSupplier(id), data, TAG);
|
||||
return data;
|
||||
return executeCall(supplierApi.deleteSupplier(id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,19 @@ import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
import com.example.petstoremobile.dtos.ErrorResponse;
|
||||
import com.google.gson.Gson;
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.UnknownHostException;
|
||||
import retrofit2.Response;
|
||||
|
||||
/**
|
||||
* Utility class for handling API error responses.
|
||||
*/
|
||||
public class ErrorUtils {
|
||||
private static final String TAG = "ErrorUtils";
|
||||
private static final Gson gson = new Gson();
|
||||
|
||||
/**
|
||||
* Shows an error message to toast based on the response.
|
||||
*/
|
||||
@@ -19,20 +26,46 @@ public class ErrorUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the error message from the response body.
|
||||
* Extracts a user-friendly error message from the response body or status code.
|
||||
*/
|
||||
public static String getErrorMessage(Response<?> response, String defaultMessage) {
|
||||
if (response == null) return defaultMessage;
|
||||
|
||||
try {
|
||||
if (response != null && response.errorBody() != null) {
|
||||
if (response.errorBody() != null) {
|
||||
String errorJson = response.errorBody().string();
|
||||
ErrorResponse errorResponse = new Gson().fromJson(errorJson, ErrorResponse.class);
|
||||
ErrorResponse errorResponse = gson.fromJson(errorJson, ErrorResponse.class);
|
||||
if (errorResponse != null && errorResponse.getMessage() != null) {
|
||||
return errorResponse.getMessage();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("ErrorUtils", "Error parsing error body", e);
|
||||
Log.e(TAG, "Error parsing error body", e);
|
||||
}
|
||||
|
||||
// Handle specific status codes if no message was provided by the API
|
||||
switch (response.code()) {
|
||||
case 401: return "Unauthorized. Please login again.";
|
||||
case 403: return "Access denied.";
|
||||
case 404: return "Resource not found.";
|
||||
case 500: return "Internal server error. Please try again later.";
|
||||
case 503: return "Service unavailable. The server might be down.";
|
||||
default: return defaultMessage + " (Code: " + response.code() + ")";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a Throwable (from onFailure) into a user-friendly network error message.
|
||||
*/
|
||||
public static String getFailureMessage(Throwable t) {
|
||||
if (t instanceof UnknownHostException || t instanceof ConnectException) {
|
||||
return "No internet connection. Please check your settings.";
|
||||
} else if (t instanceof SocketTimeoutException) {
|
||||
return "The connection timed out. Please try again.";
|
||||
} else if (t instanceof IOException) {
|
||||
return "Network error occurred. Please try again.";
|
||||
} else {
|
||||
return "An unexpected error occurred: " + t.getLocalizedMessage();
|
||||
}
|
||||
return defaultMessage;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ public class RetrofitUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* call and updates the provided MutableLiveData with Resource states.
|
||||
* Enqueues a Retrofit call and updates the provided MutableLiveData with Resource states.
|
||||
*/
|
||||
public static <T> void enqueue(@NonNull Call<T> call, @NonNull MutableLiveData<Resource<T>> data, String tag) {
|
||||
data.setValue(Resource.loading(null));
|
||||
@@ -43,8 +43,8 @@ public class RetrofitUtils {
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<T> call, @NonNull Throwable t) {
|
||||
String errorMsg = "Network Error: " + t.getMessage();
|
||||
Log.e(tag, errorMsg);
|
||||
String errorMsg = ErrorUtils.getFailureMessage(t);
|
||||
Log.e(tag, "Network Error: " + t.getMessage(), t);
|
||||
data.setValue(Resource.error(errorMsg, null));
|
||||
}
|
||||
});
|
||||
@@ -52,6 +52,7 @@ public class RetrofitUtils {
|
||||
|
||||
/**
|
||||
* Creates a callback for Retrofit calls that handles errors and logging.
|
||||
* @deprecated Use {@link #enqueue(Call, MutableLiveData, String)} for LiveData-based architecture.
|
||||
*/
|
||||
@Deprecated
|
||||
public static <T> Callback<T> createCallback(Context context, String tag, String successMsg, SuccessCallback<T> successCallback) {
|
||||
@@ -73,8 +74,9 @@ public class RetrofitUtils {
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<T> call, @NonNull Throwable t) {
|
||||
Log.e(tag, "Network Error: " + t.getMessage());
|
||||
Toast.makeText(context, "Network error. Please try again.", Toast.LENGTH_SHORT).show();
|
||||
String errorMsg = ErrorUtils.getFailureMessage(t);
|
||||
Log.e(tag, "Network Error: " + t.getMessage(), t);
|
||||
Toast.makeText(context, errorMsg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -96,7 +98,7 @@ public class RetrofitUtils {
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<T> call, @NonNull Throwable t) {
|
||||
Log.e(tag, "Network Error: " + t.getMessage());
|
||||
Log.e(tag, "Network Error: " + t.getMessage(), t);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.example.petstoremobile.viewmodels;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
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.repositories.ChatRepository;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel;
|
||||
import okhttp3.MultipartBody;
|
||||
import okhttp3.RequestBody;
|
||||
|
||||
/**
|
||||
* ViewModel for managing chat-related UI state and data operations.
|
||||
*/
|
||||
@HiltViewModel
|
||||
public class ChatViewModel extends ViewModel {
|
||||
private final ChatRepository repository;
|
||||
|
||||
@Inject
|
||||
public ChatViewModel(ChatRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all chat conversations for the current user.
|
||||
*/
|
||||
public LiveData<Resource<List<ConversationDTO>>> getAllConversations() {
|
||||
return repository.getAllConversations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the message history for a specific conversation.
|
||||
*/
|
||||
public LiveData<Resource<List<MessageDTO>>> getMessages(Long conversationId) {
|
||||
return repository.getMessages(conversationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a plain text message to a conversation.
|
||||
*/
|
||||
public LiveData<Resource<MessageDTO>> sendMessage(Long conversationId, SendMessageRequest request) {
|
||||
return repository.sendMessage(conversationId, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message with a file attachment to a conversation.
|
||||
*/
|
||||
public LiveData<Resource<MessageDTO>> sendMessageWithAttachment(Long conversationId, RequestBody content, MultipartBody.Part file) {
|
||||
return repository.sendMessageWithAttachment(conversationId, content, file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a paginated list of customers.
|
||||
*/
|
||||
public LiveData<Resource<PageResponse<CustomerDTO>>> getAllCustomers(int page, int size) {
|
||||
return repository.getAllCustomers(page, size);
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,16 @@ junitVersion = "1.3.0"
|
||||
espressoCore = "3.7.0"
|
||||
appcompat = "1.7.1"
|
||||
material = "1.13.0"
|
||||
activity = "1.12.4"
|
||||
activity = "1.13.0"
|
||||
constraintlayout = "2.2.1"
|
||||
swiperefreshlayout = "1.2.0"
|
||||
hilt = "2.51.1"
|
||||
navigation = "2.8.8"
|
||||
retrofit = "2.11.0"
|
||||
okhttp = "4.12.0"
|
||||
glide = "4.16.0"
|
||||
viewpager2 = "1.1.0"
|
||||
camera = "1.4.1"
|
||||
|
||||
[libraries]
|
||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
@@ -25,6 +30,23 @@ hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.r
|
||||
navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "navigation" }
|
||||
navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigation" }
|
||||
|
||||
# Networking
|
||||
retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
|
||||
retrofit-gson = { group = "com.squareup.retrofit2", name = "converter-gson", version.ref = "retrofit" }
|
||||
okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }
|
||||
okhttp-logging = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" }
|
||||
|
||||
# UI Components
|
||||
viewpager2 = { group = "androidx.viewpager2", name = "viewpager2", version.ref = "viewpager2" }
|
||||
glide = { group = "com.github.bumptech.glide", name = "glide", version.ref = "glide" }
|
||||
glide-compiler = { group = "com.github.bumptech.glide", name = "compiler", version.ref = "glide" }
|
||||
|
||||
# CameraX
|
||||
camera-core = { group = "androidx.camera", name = "camera-core", version.ref = "camera" }
|
||||
camera-camera2 = { group = "androidx.camera", name = "camera-camera2", version.ref = "camera" }
|
||||
camera-lifecycle = { group = "androidx.camera", name = "camera-lifecycle", version.ref = "camera" }
|
||||
camera-view = { group = "androidx.camera", name = "camera-view", version.ref = "camera" }
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#Sun Mar 01 14:36:37 MST 2026
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=a17ddd85a26b6a7f5ddb71ff8b05fc5104c0202c6e64782429790c933686c806
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
Reference in New Issue
Block a user