Merge branch 'main' into AttachmentsToChat
This commit is contained in:
@@ -16,13 +16,11 @@ import retrofit2.http.Query;
|
|||||||
|
|
||||||
public interface InventoryApi {
|
public interface InventoryApi {
|
||||||
|
|
||||||
// GET /api/v1/inventory?q=...&page=...&size=...&category=...&storeId=...&sort=...
|
|
||||||
@GET("api/v1/inventory")
|
@GET("api/v1/inventory")
|
||||||
Call<PageResponse<InventoryDTO>> getAllInventory(
|
Call<PageResponse<InventoryDTO>> getAllInventory(
|
||||||
@Query("page") int page,
|
@Query("page") int page,
|
||||||
@Query("size") int size,
|
@Query("size") int size,
|
||||||
@Query("q") String query,
|
@Query("q") String query,
|
||||||
@Query("category") String category,
|
|
||||||
@Query("storeId") Long storeId,
|
@Query("storeId") Long storeId,
|
||||||
@Query("sort") String sort);
|
@Query("sort") String sort);
|
||||||
|
|
||||||
|
|||||||
@@ -3,14 +3,10 @@ package com.example.petstoremobile.api;
|
|||||||
import com.example.petstoremobile.dtos.MessageDTO;
|
import com.example.petstoremobile.dtos.MessageDTO;
|
||||||
import com.example.petstoremobile.dtos.SendMessageRequest;
|
import com.example.petstoremobile.dtos.SendMessageRequest;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import okhttp3.MultipartBody;
|
|
||||||
import okhttp3.RequestBody;
|
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.http.Body;
|
import retrofit2.http.Body;
|
||||||
import retrofit2.http.GET;
|
import retrofit2.http.GET;
|
||||||
import retrofit2.http.Multipart;
|
|
||||||
import retrofit2.http.POST;
|
import retrofit2.http.POST;
|
||||||
import retrofit2.http.Part;
|
|
||||||
import retrofit2.http.Path;
|
import retrofit2.http.Path;
|
||||||
|
|
||||||
//api calls to get and send messages
|
//api calls to get and send messages
|
||||||
@@ -21,12 +17,4 @@ public interface MessageApi {
|
|||||||
|
|
||||||
@POST("api/v1/chat/conversations/{id}/messages")
|
@POST("api/v1/chat/conversations/{id}/messages")
|
||||||
Call<MessageDTO> sendMessage(@Path("id") Long conversationId, @Body SendMessageRequest request);
|
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
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ public interface PurchaseOrderApi {
|
|||||||
Call<PageResponse<PurchaseOrderDTO>> getAllPurchaseOrders(
|
Call<PageResponse<PurchaseOrderDTO>> getAllPurchaseOrders(
|
||||||
@Query("page") int page,
|
@Query("page") int page,
|
||||||
@Query("size") int size,
|
@Query("size") int size,
|
||||||
@Query("query") String query,
|
@Query("q") String query,
|
||||||
@Query("storeId") Long storeId,
|
@Query("storeId") Long storeId,
|
||||||
@Query("sort") String sort);
|
@Query("sort") String sort);
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,8 @@ import com.example.petstoremobile.dtos.SaleDTO;
|
|||||||
|
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.http.Body;
|
import retrofit2.http.Body;
|
||||||
import retrofit2.http.DELETE;
|
|
||||||
import retrofit2.http.GET;
|
import retrofit2.http.GET;
|
||||||
import retrofit2.http.POST;
|
import retrofit2.http.POST;
|
||||||
import retrofit2.http.PUT;
|
|
||||||
import retrofit2.http.Path;
|
import retrofit2.http.Path;
|
||||||
import retrofit2.http.Query;
|
import retrofit2.http.Query;
|
||||||
|
|
||||||
@@ -24,10 +22,4 @@ public interface SaleApi {
|
|||||||
|
|
||||||
@POST("api/v1/sales")
|
@POST("api/v1/sales")
|
||||||
Call<SaleDTO> createSale(@Body SaleDTO sale);
|
Call<SaleDTO> createSale(@Body SaleDTO sale);
|
||||||
|
|
||||||
@PUT("api/v1/sales/{id}")
|
|
||||||
Call<SaleDTO> updateSale(@Path("id") Long id, @Body SaleDTO sale);
|
|
||||||
|
|
||||||
@DELETE("api/v1/sales/{id}")
|
|
||||||
Call<Void> deleteSale(@Path("id") Long id);
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.example.petstoremobile.api.auth;
|
package com.example.petstoremobile.api.auth;
|
||||||
|
|
||||||
import com.example.petstoremobile.dtos.AuthDTO;
|
import com.example.petstoremobile.dtos.AuthDTO;
|
||||||
|
import com.example.petstoremobile.dtos.AvatarUploadResponse;
|
||||||
import com.example.petstoremobile.dtos.UserDTO;
|
import com.example.petstoremobile.dtos.UserDTO;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -36,7 +37,7 @@ public interface AuthApi {
|
|||||||
//upload avatar endpoint
|
//upload avatar endpoint
|
||||||
@Multipart
|
@Multipart
|
||||||
@POST("api/v1/auth/me/avatar")
|
@POST("api/v1/auth/me/avatar")
|
||||||
Call<UserDTO> uploadAvatar(@Part MultipartBody.Part avatar);
|
Call<AvatarUploadResponse> uploadAvatar(@Part MultipartBody.Part avatar);
|
||||||
|
|
||||||
//delete avatar endpoint
|
//delete avatar endpoint
|
||||||
@DELETE("api/v1/auth/me/avatar")
|
@DELETE("api/v1/auth/me/avatar")
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.example.petstoremobile.dtos;
|
||||||
|
|
||||||
|
public class AvatarUploadResponse {
|
||||||
|
private String avatarUrl;
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
public AvatarUploadResponse() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAvatarUrl() {
|
||||||
|
return avatarUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAvatarUrl(String avatarUrl) {
|
||||||
|
this.avatarUrl = avatarUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import android.provider.OpenableColumns;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.*;
|
import android.view.*;
|
||||||
import android.view.inputmethod.EditorInfo;
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
import android.widget.Toast;
|
||||||
import androidx.activity.result.ActivityResultLauncher;
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
import androidx.activity.result.contract.ActivityResultContracts;
|
import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@@ -28,21 +29,16 @@ import com.example.petstoremobile.dtos.SendMessageRequest;
|
|||||||
import com.example.petstoremobile.models.Chat;
|
import com.example.petstoremobile.models.Chat;
|
||||||
import com.example.petstoremobile.models.Message;
|
import com.example.petstoremobile.models.Message;
|
||||||
import com.example.petstoremobile.services.ChatNotificationService;
|
import com.example.petstoremobile.services.ChatNotificationService;
|
||||||
import com.example.petstoremobile.utils.FileUtils;
|
|
||||||
import com.example.petstoremobile.utils.Resource;
|
import com.example.petstoremobile.utils.Resource;
|
||||||
import com.example.petstoremobile.viewmodels.ChatViewModel;
|
import com.example.petstoremobile.viewmodels.ChatViewModel;
|
||||||
import com.example.petstoremobile.websocket.StompChatManager;
|
import com.example.petstoremobile.websocket.StompChatManager;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint;
|
import dagger.hilt.android.AndroidEntryPoint;
|
||||||
import okhttp3.MediaType;
|
|
||||||
import okhttp3.MultipartBody;
|
|
||||||
import okhttp3.RequestBody;
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickListener, StompChatManager.MessageListener,
|
public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickListener, StompChatManager.MessageListener,
|
||||||
@@ -361,26 +357,10 @@ public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickLis
|
|||||||
binding.etMessage.setText("");
|
binding.etMessage.setText("");
|
||||||
removeAttachment();
|
removeAttachment();
|
||||||
|
|
||||||
try {
|
if (!text.isEmpty()) {
|
||||||
File file = FileUtils.getFileFromUri(requireContext(), uri);
|
binding.etMessage.setText(text);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
Toast.makeText(requireContext(), "File attachments are not supported", Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -230,9 +230,7 @@ public class ProfileFragment extends Fragment {
|
|||||||
viewModel.uploadAvatar(body).observe(getViewLifecycleOwner(), resource -> {
|
viewModel.uploadAvatar(body).observe(getViewLifecycleOwner(), resource -> {
|
||||||
if (resource == null) return;
|
if (resource == null) return;
|
||||||
if (resource.status == Resource.Status.SUCCESS) {
|
if (resource.status == Resource.Status.SUCCESS) {
|
||||||
currentUser = resource.data;
|
|
||||||
Toast.makeText(getContext(), "Avatar updated successfully", Toast.LENGTH_SHORT).show();
|
Toast.makeText(getContext(), "Avatar updated successfully", Toast.LENGTH_SHORT).show();
|
||||||
// Reload image after successful upload
|
|
||||||
loadProfileData();
|
loadProfileData();
|
||||||
} else if (resource.status == Resource.Status.ERROR) {
|
} else if (resource.status == Resource.Status.ERROR) {
|
||||||
Toast.makeText(getContext(), "Upload failed: " + resource.message, Toast.LENGTH_SHORT).show();
|
Toast.makeText(getContext(), "Upload failed: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Load all inventory items from the backend using viewModel
|
//Load all inventory items from the backend using viewModel
|
||||||
viewModel.getAllInventory(query, null, storeId, currentPage, PAGE_SIZE, "product.prodName").observe(getViewLifecycleOwner(), resource -> {
|
viewModel.getAllInventory(query, storeId, currentPage, PAGE_SIZE, "product.prodName").observe(getViewLifecycleOwner(), resource -> {
|
||||||
if (resource == null) return;
|
if (resource == null) return;
|
||||||
|
|
||||||
// Check the status to see if the resource is loaded and display the data
|
// Check the status to see if the resource is loaded and display the data
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import androidx.lifecycle.MutableLiveData;
|
|||||||
import com.example.petstoremobile.api.auth.AuthApi;
|
import com.example.petstoremobile.api.auth.AuthApi;
|
||||||
import com.example.petstoremobile.api.auth.TokenManager;
|
import com.example.petstoremobile.api.auth.TokenManager;
|
||||||
import com.example.petstoremobile.dtos.AuthDTO;
|
import com.example.petstoremobile.dtos.AuthDTO;
|
||||||
|
import com.example.petstoremobile.dtos.AvatarUploadResponse;
|
||||||
import com.example.petstoremobile.dtos.UserDTO;
|
import com.example.petstoremobile.dtos.UserDTO;
|
||||||
import com.example.petstoremobile.utils.ErrorUtils;
|
import com.example.petstoremobile.utils.ErrorUtils;
|
||||||
import com.example.petstoremobile.utils.Resource;
|
import com.example.petstoremobile.utils.Resource;
|
||||||
@@ -83,7 +84,7 @@ public class AuthRepository extends BaseRepository {
|
|||||||
/**
|
/**
|
||||||
* Uploads a multipart image to be used as the current user's avatar.
|
* Uploads a multipart image to be used as the current user's avatar.
|
||||||
*/
|
*/
|
||||||
public LiveData<Resource<UserDTO>> uploadAvatar(MultipartBody.Part avatar) {
|
public LiveData<Resource<AvatarUploadResponse>> uploadAvatar(MultipartBody.Part avatar) {
|
||||||
return executeCall(authApi.uploadAvatar(avatar));
|
return executeCall(authApi.uploadAvatar(avatar));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,6 @@ import java.util.List;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import okhttp3.MultipartBody;
|
|
||||||
import okhttp3.RequestBody;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repository for handling chat-related data operations.
|
* Repository for handling chat-related data operations.
|
||||||
*/
|
*/
|
||||||
@@ -58,13 +55,6 @@ public class ChatRepository extends BaseRepository {
|
|||||||
return executeCall(messageApi.sendMessage(conversationId, 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.
|
* Fetches a paginated list of customers.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ public class InventoryRepository extends BaseRepository {
|
|||||||
/**
|
/**
|
||||||
* Retrieves a paginated list of inventory items from the API with optional search, category, storeId and sort.
|
* Retrieves a paginated list of inventory items from the API with optional search, category, storeId and sort.
|
||||||
*/
|
*/
|
||||||
public LiveData<Resource<PageResponse<InventoryDTO>>> getAllInventory(String query, String category, Long storeId, int page, int size, String sort) {
|
public LiveData<Resource<PageResponse<InventoryDTO>>> getAllInventory(String query, Long storeId, int page, int size, String sort) {
|
||||||
return executeCall(inventoryApi.getAllInventory(page, size, query, category, storeId, sort));
|
return executeCall(inventoryApi.getAllInventory(page, size, query, storeId, sort));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import androidx.lifecycle.LiveData;
|
|||||||
import androidx.lifecycle.ViewModel;
|
import androidx.lifecycle.ViewModel;
|
||||||
|
|
||||||
import com.example.petstoremobile.dtos.AuthDTO;
|
import com.example.petstoremobile.dtos.AuthDTO;
|
||||||
|
import com.example.petstoremobile.dtos.AvatarUploadResponse;
|
||||||
import com.example.petstoremobile.dtos.UserDTO;
|
import com.example.petstoremobile.dtos.UserDTO;
|
||||||
import com.example.petstoremobile.repositories.AuthRepository;
|
import com.example.petstoremobile.repositories.AuthRepository;
|
||||||
import com.example.petstoremobile.utils.Resource;
|
import com.example.petstoremobile.utils.Resource;
|
||||||
@@ -48,7 +49,7 @@ public class AuthViewModel extends ViewModel {
|
|||||||
/**
|
/**
|
||||||
* Uploads a new avatar image for the current user.
|
* Uploads a new avatar image for the current user.
|
||||||
*/
|
*/
|
||||||
public LiveData<Resource<UserDTO>> uploadAvatar(MultipartBody.Part avatar) {
|
public LiveData<Resource<AvatarUploadResponse>> uploadAvatar(MultipartBody.Part avatar) {
|
||||||
return repository.uploadAvatar(avatar);
|
return repository.uploadAvatar(avatar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,9 +16,6 @@ import java.util.List;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel;
|
import dagger.hilt.android.lifecycle.HiltViewModel;
|
||||||
import okhttp3.MultipartBody;
|
|
||||||
import okhttp3.RequestBody;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ViewModel for managing chat-related UI state and data operations.
|
* ViewModel for managing chat-related UI state and data operations.
|
||||||
*/
|
*/
|
||||||
@@ -52,13 +49,6 @@ public class ChatViewModel extends ViewModel {
|
|||||||
return repository.sendMessage(conversationId, 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.
|
* Fetches a paginated list of customers.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ public class InventoryViewModel extends ViewModel {
|
|||||||
/**
|
/**
|
||||||
* Retrieves a paginated list of inventory items, with optional filtering and sorting.
|
* Retrieves a paginated list of inventory items, with optional filtering and sorting.
|
||||||
*/
|
*/
|
||||||
public LiveData<Resource<PageResponse<InventoryDTO>>> getAllInventory(String query, String category, Long storeId, int page, int size, String sort) {
|
public LiveData<Resource<PageResponse<InventoryDTO>>> getAllInventory(String query, Long storeId, int page, int size, String sort) {
|
||||||
return inventoryRepository.getAllInventory(query, category, storeId, page, size, sort);
|
return inventoryRepository.getAllInventory(query, storeId, page, size, sort);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,7 @@ import com.petshop.backend.repository.UserRepository;
|
|||||||
import com.petshop.backend.security.JwtUtil;
|
import com.petshop.backend.security.JwtUtil;
|
||||||
import com.petshop.backend.service.AvatarStorageService;
|
import com.petshop.backend.service.AvatarStorageService;
|
||||||
import com.petshop.backend.util.AuthenticationHelper;
|
import com.petshop.backend.util.AuthenticationHelper;
|
||||||
|
import com.petshop.backend.util.PhoneUtils;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
@@ -53,19 +54,23 @@ public class AuthController {
|
|||||||
|
|
||||||
@PostMapping("/register")
|
@PostMapping("/register")
|
||||||
public ResponseEntity<?> register(@Valid @RequestBody RegisterRequest request) {
|
public ResponseEntity<?> register(@Valid @RequestBody RegisterRequest request) {
|
||||||
if (userRepository.findByUsername(request.getUsername()).isPresent()) {
|
String username = trimToNull(request.getUsername());
|
||||||
|
String email = trimToNull(request.getEmail());
|
||||||
|
NameParts nameParts = splitFullName(request.getFullName());
|
||||||
|
String phone = normalizePhone(request.getPhone());
|
||||||
|
|
||||||
|
if (userRepository.findByUsername(username).isPresent()) {
|
||||||
Map<String, String> error = new HashMap<>();
|
Map<String, String> error = new HashMap<>();
|
||||||
error.put("message", "Username already exists");
|
error.put("message", "Username already exists");
|
||||||
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userRepository.findByEmail(request.getEmail()).isPresent()) {
|
if (userRepository.findByEmail(email).isPresent()) {
|
||||||
Map<String, String> error = new HashMap<>();
|
Map<String, String> error = new HashMap<>();
|
||||||
error.put("message", "Email already exists");
|
error.put("message", "Email already exists");
|
||||||
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
String phone = trimToNull(request.getPhone());
|
|
||||||
if (phone != null && userRepository.findByPhone(phone).isPresent()) {
|
if (phone != null && userRepository.findByPhone(phone).isPresent()) {
|
||||||
Map<String, String> error = new HashMap<>();
|
Map<String, String> error = new HashMap<>();
|
||||||
error.put("message", "Phone already exists");
|
error.put("message", "Phone already exists");
|
||||||
@@ -73,10 +78,12 @@ public class AuthController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
User user = new User();
|
User user = new User();
|
||||||
user.setUsername(request.getUsername());
|
user.setUsername(username);
|
||||||
user.setPassword(passwordEncoder.encode(request.getPassword()));
|
user.setPassword(passwordEncoder.encode(request.getPassword()));
|
||||||
user.setEmail(request.getEmail());
|
user.setEmail(email);
|
||||||
user.setFullName(request.getFullName());
|
user.setFirstName(nameParts.firstName());
|
||||||
|
user.setLastName(nameParts.lastName());
|
||||||
|
user.setFullName(nameParts.fullName());
|
||||||
user.setPhone(phone);
|
user.setPhone(phone);
|
||||||
user.setRole(User.Role.CUSTOMER);
|
user.setRole(User.Role.CUSTOMER);
|
||||||
user.setActive(true);
|
user.setActive(true);
|
||||||
@@ -143,31 +150,36 @@ public class AuthController {
|
|||||||
User user = getAuthenticatedUser();
|
User user = getAuthenticatedUser();
|
||||||
boolean invalidateToken = false;
|
boolean invalidateToken = false;
|
||||||
|
|
||||||
if (request.getUsername() != null && !request.getUsername().equals(user.getUsername())) {
|
String username = trimToNull(request.getUsername());
|
||||||
if (userRepository.findByUsername(request.getUsername()).isPresent()) {
|
if (username != null && !username.equals(user.getUsername())) {
|
||||||
|
if (userRepository.findByUsername(username).isPresent()) {
|
||||||
Map<String, String> error = new HashMap<>();
|
Map<String, String> error = new HashMap<>();
|
||||||
error.put("message", "Username already exists");
|
error.put("message", "Username already exists");
|
||||||
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
||||||
}
|
}
|
||||||
user.setUsername(request.getUsername());
|
user.setUsername(username);
|
||||||
invalidateToken = true;
|
invalidateToken = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.getEmail() != null && !request.getEmail().equals(user.getEmail())) {
|
String email = trimToNull(request.getEmail());
|
||||||
if (userRepository.findByEmail(request.getEmail()).isPresent()) {
|
if (email != null && !email.equals(user.getEmail())) {
|
||||||
|
if (userRepository.findByEmail(email).isPresent()) {
|
||||||
Map<String, String> error = new HashMap<>();
|
Map<String, String> error = new HashMap<>();
|
||||||
error.put("message", "Email already exists");
|
error.put("message", "Email already exists");
|
||||||
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
||||||
}
|
}
|
||||||
user.setEmail(request.getEmail());
|
user.setEmail(email);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.getFullName() != null) {
|
if (request.getFullName() != null) {
|
||||||
user.setFullName(request.getFullName());
|
NameParts nameParts = splitFullName(request.getFullName());
|
||||||
|
user.setFirstName(nameParts.firstName());
|
||||||
|
user.setLastName(nameParts.lastName());
|
||||||
|
user.setFullName(nameParts.fullName());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.getPhone() != null) {
|
if (request.getPhone() != null) {
|
||||||
String phone = trimToNull(request.getPhone());
|
String phone = normalizePhone(request.getPhone());
|
||||||
if (!java.util.Objects.equals(phone, user.getPhone())) {
|
if (!java.util.Objects.equals(phone, user.getPhone())) {
|
||||||
if (phone != null && userRepository.findByPhone(phone)
|
if (phone != null && userRepository.findByPhone(phone)
|
||||||
.filter(existing -> !existing.getId().equals(user.getId()))
|
.filter(existing -> !existing.getId().equals(user.getId()))
|
||||||
@@ -196,11 +208,15 @@ public class AuthController {
|
|||||||
private UserInfoResponse toUserInfoResponse(User user) {
|
private UserInfoResponse toUserInfoResponse(User user) {
|
||||||
StoreLocation primaryStore = user.getPrimaryStore();
|
StoreLocation primaryStore = user.getPrimaryStore();
|
||||||
Long customerId = user.getRole() == User.Role.CUSTOMER ? user.getId() : null;
|
Long customerId = user.getRole() == User.Role.CUSTOMER ? user.getId() : null;
|
||||||
|
String fullName = user.getFullName();
|
||||||
|
if (fullName == null || fullName.isBlank()) {
|
||||||
|
fullName = joinFullName(user.getFirstName(), user.getLastName());
|
||||||
|
}
|
||||||
return new UserInfoResponse(
|
return new UserInfoResponse(
|
||||||
user.getId(),
|
user.getId(),
|
||||||
user.getUsername(),
|
user.getUsername(),
|
||||||
user.getEmail(),
|
user.getEmail(),
|
||||||
user.getFullName(),
|
fullName,
|
||||||
user.getPhone(),
|
user.getPhone(),
|
||||||
avatarStorageService.toOwnerAvatarUrl(user),
|
avatarStorageService.toOwnerAvatarUrl(user),
|
||||||
user.getRole().name(),
|
user.getRole().name(),
|
||||||
@@ -218,6 +234,36 @@ public class AuthController {
|
|||||||
return trimmed.isEmpty() ? null : trimmed;
|
return trimmed.isEmpty() ? null : trimmed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String normalizePhone(String value) {
|
||||||
|
return trimToNull(PhoneUtils.normalize(trimToNull(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private NameParts splitFullName(String value) {
|
||||||
|
String normalized = trimToNull(value);
|
||||||
|
if (normalized == null) {
|
||||||
|
throw new IllegalArgumentException("Full name is required");
|
||||||
|
}
|
||||||
|
String[] parts = normalized.split("\\s+", 2);
|
||||||
|
String firstName = parts[0];
|
||||||
|
String lastName = parts.length > 1 ? parts[1] : "";
|
||||||
|
return new NameParts(firstName, lastName, joinFullName(firstName, lastName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String joinFullName(String firstName, String lastName) {
|
||||||
|
String first = trimToNull(firstName);
|
||||||
|
String last = trimToNull(lastName);
|
||||||
|
if (first == null) {
|
||||||
|
return last == null ? null : last;
|
||||||
|
}
|
||||||
|
if (last == null) {
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
return first + " " + last;
|
||||||
|
}
|
||||||
|
|
||||||
|
private record NameParts(String firstName, String lastName, String fullName) {
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/me/avatar")
|
@PostMapping("/me/avatar")
|
||||||
public ResponseEntity<?> uploadAvatar(@RequestParam("avatar") MultipartFile file) {
|
public ResponseEntity<?> uploadAvatar(@RequestParam("avatar") MultipartFile file) {
|
||||||
User user = getAuthenticatedUser();
|
User user = getAuthenticatedUser();
|
||||||
|
|||||||
@@ -153,6 +153,16 @@ public class DropdownController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/customers/{customerId}/pets")
|
||||||
|
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
||||||
|
public ResponseEntity<List<DropdownOption>> getCustomerPets(@PathVariable Long customerId) {
|
||||||
|
return ResponseEntity.ok(
|
||||||
|
petRepository.findAllByOwner_IdOrderByPetNameAsc(customerId).stream()
|
||||||
|
.map(p -> new DropdownOption(p.getPetId(), p.getPetName()))
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/suppliers")
|
@GetMapping("/suppliers")
|
||||||
@PreAuthorize("hasRole('ADMIN')")
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
public ResponseEntity<List<DropdownOption>> getSuppliers() {
|
public ResponseEntity<List<DropdownOption>> getSuppliers() {
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package com.petshop.backend.controller;
|
||||||
|
|
||||||
|
import com.petshop.backend.dto.pet.MyPetRequest;
|
||||||
|
import com.petshop.backend.dto.pet.MyPetResponse;
|
||||||
|
import com.petshop.backend.entity.User;
|
||||||
|
import com.petshop.backend.repository.UserRepository;
|
||||||
|
import com.petshop.backend.service.PetService;
|
||||||
|
import com.petshop.backend.util.AuthenticationHelper;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PutMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/my-pets")
|
||||||
|
@PreAuthorize("isAuthenticated()")
|
||||||
|
public class MyPetController {
|
||||||
|
|
||||||
|
private final PetService petService;
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
|
||||||
|
public MyPetController(PetService petService, UserRepository userRepository) {
|
||||||
|
this.petService = petService;
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public ResponseEntity<List<MyPetResponse>> getMyPets() {
|
||||||
|
return ResponseEntity.ok(petService.getMyPets(currentUserId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public ResponseEntity<MyPetResponse> createMyPet(@Valid @RequestBody MyPetRequest request) {
|
||||||
|
return ResponseEntity.ok(petService.createMyPet(currentUserId(), request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public ResponseEntity<MyPetResponse> updateMyPet(@PathVariable Long id, @Valid @RequestBody MyPetRequest request) {
|
||||||
|
return ResponseEntity.ok(petService.updateMyPet(currentUserId(), id, request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public ResponseEntity<Void> deleteMyPet(@PathVariable Long id) {
|
||||||
|
petService.deleteMyPet(currentUserId(), id);
|
||||||
|
return ResponseEntity.noContent().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{id}/image")
|
||||||
|
public ResponseEntity<?> uploadMyPetImage(@PathVariable Long id, @RequestParam("image") MultipartFile image) {
|
||||||
|
try {
|
||||||
|
return ResponseEntity.ok(petService.uploadMyPetImage(currentUserId(), id, image));
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
return ResponseEntity.badRequest().body(Map.of("message", ex.getMessage()));
|
||||||
|
} catch (IOException ex) {
|
||||||
|
return ResponseEntity.badRequest().body(Map.of("message", "Failed to upload pet image: " + ex.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long currentUserId() {
|
||||||
|
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
|
||||||
|
return user.getId();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -48,12 +48,8 @@ public class PetImageController {
|
|||||||
|
|
||||||
@GetMapping("/{id}/image")
|
@GetMapping("/{id}/image")
|
||||||
public ResponseEntity<Resource> getPetImage(@PathVariable Long id) {
|
public ResponseEntity<Resource> getPetImage(@PathVariable Long id) {
|
||||||
try {
|
PetService.ImagePayload payload = petService.loadPetImage(id, currentUserId(), currentUserRole());
|
||||||
PetService.ImagePayload payload = petService.loadPetImage(id, currentUserId(), currentUserRole());
|
return ResponseEntity.ok().contentType(payload.mediaType()).body(payload.resource());
|
||||||
return ResponseEntity.ok().contentType(payload.mediaType()).body(payload.resource());
|
|
||||||
} catch (PetService.ForbiddenImageAccessException ex) {
|
|
||||||
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{id}/image")
|
@DeleteMapping("/{id}/image")
|
||||||
|
|||||||
@@ -15,9 +15,7 @@ import org.springframework.security.core.Authentication;
|
|||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/v1/refunds")
|
@RequestMapping("/api/v1/refunds")
|
||||||
@@ -33,27 +31,20 @@ public class RefundController {
|
|||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
|
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
|
||||||
public ResponseEntity<?> createRefund(@Valid @RequestBody RefundRequest request) {
|
public ResponseEntity<RefundResponse> createRefund(@Valid @RequestBody RefundRequest request) {
|
||||||
try {
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
String role = authentication.getAuthorities().stream()
|
||||||
String role = authentication.getAuthorities().stream()
|
.findFirst()
|
||||||
.findFirst()
|
.map(authority -> authority.getAuthority().replace("ROLE_", ""))
|
||||||
.map(authority -> authority.getAuthority().replace("ROLE_", ""))
|
.orElse(null);
|
||||||
.orElse(null);
|
|
||||||
|
|
||||||
Long customerId = null;
|
Long customerId = null;
|
||||||
if (role != null && role.equals("CUSTOMER")) {
|
if (role != null && role.equals("CUSTOMER")) {
|
||||||
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
|
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
|
||||||
customerId = user.getId();
|
customerId = user.getId();
|
||||||
}
|
|
||||||
|
|
||||||
RefundResponse refund = refundService.createRefund(request, customerId);
|
|
||||||
return ResponseEntity.status(HttpStatus.CREATED).body(refund);
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
Map<String, String> error = new HashMap<>();
|
|
||||||
error.put("message", e.getMessage());
|
|
||||||
return ResponseEntity.badRequest().body(error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ResponseEntity.status(HttpStatus.CREATED).body(refundService.createRefund(request, customerId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
@@ -77,54 +68,32 @@ public class RefundController {
|
|||||||
|
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
|
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
|
||||||
public ResponseEntity<?> getRefundById(@PathVariable Long id) {
|
public ResponseEntity<RefundResponse> getRefundById(@PathVariable Long id) {
|
||||||
try {
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
String role = authentication.getAuthorities().stream()
|
||||||
String role = authentication.getAuthorities().stream()
|
.findFirst()
|
||||||
.findFirst()
|
.map(authority -> authority.getAuthority().replace("ROLE_", ""))
|
||||||
.map(authority -> authority.getAuthority().replace("ROLE_", ""))
|
.orElse(null);
|
||||||
.orElse(null);
|
|
||||||
|
|
||||||
Long customerId = null;
|
Long customerId = null;
|
||||||
if (role != null && role.equals("CUSTOMER")) {
|
if (role != null && role.equals("CUSTOMER")) {
|
||||||
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
|
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
|
||||||
customerId = user.getId();
|
customerId = user.getId();
|
||||||
}
|
|
||||||
|
|
||||||
RefundResponse refund = refundService.getRefundById(id, customerId);
|
|
||||||
return ResponseEntity.ok(refund);
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
Map<String, String> error = new HashMap<>();
|
|
||||||
error.put("message", e.getMessage());
|
|
||||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ResponseEntity.ok(refundService.getRefundById(id, customerId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/{id}")
|
@PutMapping("/{id}")
|
||||||
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
||||||
public ResponseEntity<?> updateRefund(@PathVariable Long id, @Valid @RequestBody RefundUpdateRequest request) {
|
public ResponseEntity<RefundResponse> updateRefund(@PathVariable Long id, @Valid @RequestBody RefundUpdateRequest request) {
|
||||||
try {
|
return ResponseEntity.ok(refundService.updateRefundStatus(id, request.getStatus()));
|
||||||
RefundResponse refund = refundService.updateRefundStatus(id, request.getStatus());
|
|
||||||
return ResponseEntity.ok(refund);
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
Map<String, String> error = new HashMap<>();
|
|
||||||
error.put("message", e.getMessage());
|
|
||||||
return ResponseEntity.badRequest().body(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
@PreAuthorize("hasRole('ADMIN')")
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
public ResponseEntity<?> deleteRefund(@PathVariable Long id) {
|
public ResponseEntity<Void> deleteRefund(@PathVariable Long id) {
|
||||||
try {
|
refundService.deleteRefund(id);
|
||||||
refundService.deleteRefund(id);
|
return ResponseEntity.noContent().build();
|
||||||
Map<String, String> response = new HashMap<>();
|
|
||||||
response.put("message", "Refund deleted successfully");
|
|
||||||
return ResponseEntity.ok(response);
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
Map<String, String> error = new HashMap<>();
|
|
||||||
error.put("message", e.getMessage());
|
|
||||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package com.petshop.backend.dto.pet;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
|
||||||
|
public class MyPetRequest {
|
||||||
|
|
||||||
|
@NotBlank(message = "Pet name is required")
|
||||||
|
@Size(max = 50, message = "Pet name must not exceed 50 characters")
|
||||||
|
private String petName;
|
||||||
|
|
||||||
|
@NotBlank(message = "Species is required")
|
||||||
|
@Size(max = 50, message = "Species must not exceed 50 characters")
|
||||||
|
private String species;
|
||||||
|
|
||||||
|
@Size(max = 50, message = "Breed must not exceed 50 characters")
|
||||||
|
private String breed;
|
||||||
|
|
||||||
|
public String getPetName() {
|
||||||
|
return petName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPetName(String petName) {
|
||||||
|
this.petName = petName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSpecies() {
|
||||||
|
return species;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpecies(String species) {
|
||||||
|
this.species = species;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBreed() {
|
||||||
|
return breed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBreed(String breed) {
|
||||||
|
this.breed = breed;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package com.petshop.backend.dto.pet;
|
||||||
|
|
||||||
|
public class MyPetResponse {
|
||||||
|
|
||||||
|
private Long customerPetId;
|
||||||
|
private String petName;
|
||||||
|
private String species;
|
||||||
|
private String breed;
|
||||||
|
private String imageUrl;
|
||||||
|
|
||||||
|
public MyPetResponse() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public MyPetResponse(Long customerPetId, String petName, String species, String breed, String imageUrl) {
|
||||||
|
this.customerPetId = customerPetId;
|
||||||
|
this.petName = petName;
|
||||||
|
this.species = species;
|
||||||
|
this.breed = breed;
|
||||||
|
this.imageUrl = imageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getCustomerPetId() {
|
||||||
|
return customerPetId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCustomerPetId(Long customerPetId) {
|
||||||
|
this.customerPetId = customerPetId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPetName() {
|
||||||
|
return petName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPetName(String petName) {
|
||||||
|
this.petName = petName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSpecies() {
|
||||||
|
return species;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpecies(String species) {
|
||||||
|
this.species = species;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBreed() {
|
||||||
|
return breed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBreed(String breed) {
|
||||||
|
this.breed = breed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImageUrl() {
|
||||||
|
return imageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImageUrl(String imageUrl) {
|
||||||
|
this.imageUrl = imageUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.petshop.backend.exception;
|
package com.petshop.backend.exception;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
@@ -13,7 +14,10 @@ import java.time.LocalDateTime;
|
|||||||
@Component
|
@Component
|
||||||
public class ApiErrorResponder {
|
public class ApiErrorResponder {
|
||||||
|
|
||||||
private final ObjectMapper objectMapper = JsonMapper.builder().findAndAddModules().build();
|
private final ObjectMapper objectMapper = JsonMapper.builder()
|
||||||
|
.findAndAddModules()
|
||||||
|
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
|
||||||
|
.build();
|
||||||
|
|
||||||
public void write(HttpServletResponse response, HttpStatus status, String message, String details, String path) throws IOException {
|
public void write(HttpServletResponse response, HttpStatus status, String message, String details, String path) throws IOException {
|
||||||
response.setStatus(status.value());
|
response.setStatus(status.value());
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
package com.petshop.backend.exception;
|
package com.petshop.backend.exception;
|
||||||
|
|
||||||
|
import com.petshop.backend.service.PetService;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import org.springframework.dao.DataIntegrityViolationException;
|
import org.springframework.dao.DataIntegrityViolationException;
|
||||||
|
import org.springframework.data.core.PropertyReferenceException;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.validation.FieldError;
|
import org.springframework.validation.FieldError;
|
||||||
|
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
|
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
|
||||||
import org.springframework.web.server.ResponseStatusException;
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
import org.springframework.web.servlet.resource.NoResourceFoundException;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -78,6 +82,26 @@ public class GlobalExceptionHandler {
|
|||||||
return buildErrorResponse(HttpStatus.valueOf(ex.getStatusCode().value()), message, ex, request);
|
return buildErrorResponse(HttpStatus.valueOf(ex.getStatusCode().value()), message, ex, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(NoResourceFoundException.class)
|
||||||
|
public ResponseEntity<ApiErrorResponse> handleNoResourceFound(NoResourceFoundException ex, HttpServletRequest request) {
|
||||||
|
return buildErrorResponse(HttpStatus.NOT_FOUND, "Route not found", ex, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
|
||||||
|
public ResponseEntity<ApiErrorResponse> handleMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpServletRequest request) {
|
||||||
|
return buildErrorResponse(HttpStatus.METHOD_NOT_ALLOWED, ex.getMessage(), ex, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(PropertyReferenceException.class)
|
||||||
|
public ResponseEntity<ApiErrorResponse> handleBadSortProperty(PropertyReferenceException ex, HttpServletRequest request) {
|
||||||
|
return buildErrorResponse(HttpStatus.BAD_REQUEST, "Invalid sort field: " + ex.getPropertyName(), ex, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(PetService.ForbiddenImageAccessException.class)
|
||||||
|
public ResponseEntity<ApiErrorResponse> handleForbiddenImageAccess(PetService.ForbiddenImageAccessException ex, HttpServletRequest request) {
|
||||||
|
return buildErrorResponse(HttpStatus.FORBIDDEN, "Access to this pet image is not allowed", ex, request);
|
||||||
|
}
|
||||||
|
|
||||||
@ExceptionHandler(Exception.class)
|
@ExceptionHandler(Exception.class)
|
||||||
public ResponseEntity<ApiErrorResponse> handleGenericException(Exception ex, HttpServletRequest request) {
|
public ResponseEntity<ApiErrorResponse> handleGenericException(Exception ex, HttpServletRequest request) {
|
||||||
String message = ex.getMessage() == null || ex.getMessage().isBlank()
|
String message = ex.getMessage() == null || ex.getMessage().isBlank()
|
||||||
|
|||||||
@@ -9,11 +9,14 @@ import org.springframework.data.repository.query.Param;
|
|||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface PetRepository extends JpaRepository<Pet, Long> {
|
public interface PetRepository extends JpaRepository<Pet, Long> {
|
||||||
|
|
||||||
List<Pet> findAllByPetStatusIgnoreCaseOrderByPetNameAsc(String petStatus);
|
List<Pet> findAllByPetStatusIgnoreCaseOrderByPetNameAsc(String petStatus);
|
||||||
|
List<Pet> findAllByOwner_IdOrderByPetNameAsc(Long ownerId);
|
||||||
|
Optional<Pet> findByIdAndOwner_Id(Long id, Long ownerId);
|
||||||
|
|
||||||
@Query("SELECT p FROM Pet p WHERE " +
|
@Query("SELECT p FROM Pet p WHERE " +
|
||||||
"(:q IS NULL OR LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petSpecies) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(COALESCE(p.petBreed, '')) LIKE LOWER(CONCAT('%', :q, '%'))) AND " +
|
"(:q IS NULL OR LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petSpecies) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(COALESCE(p.petBreed, '')) LIKE LOWER(CONCAT('%', :q, '%'))) AND " +
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.petshop.backend.service;
|
package com.petshop.backend.service;
|
||||||
|
|
||||||
import com.petshop.backend.dto.common.BulkDeleteRequest;
|
import com.petshop.backend.dto.common.BulkDeleteRequest;
|
||||||
|
import com.petshop.backend.dto.pet.MyPetRequest;
|
||||||
|
import com.petshop.backend.dto.pet.MyPetResponse;
|
||||||
import com.petshop.backend.dto.pet.PetRequest;
|
import com.petshop.backend.dto.pet.PetRequest;
|
||||||
import com.petshop.backend.dto.pet.PetResponse;
|
import com.petshop.backend.dto.pet.PetResponse;
|
||||||
import com.petshop.backend.entity.Adoption;
|
import com.petshop.backend.entity.Adoption;
|
||||||
@@ -25,6 +27,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@@ -82,6 +85,50 @@ public class PetService {
|
|||||||
return mapToResponse(pet);
|
return mapToResponse(pet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public List<MyPetResponse> getMyPets(Long ownerUserId) {
|
||||||
|
return petRepository.findAllByOwner_IdOrderByPetNameAsc(ownerUserId).stream()
|
||||||
|
.map(this::mapToMyPetResponse)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public MyPetResponse createMyPet(Long ownerUserId, MyPetRequest request) {
|
||||||
|
User owner = userRepository.findById(ownerUserId)
|
||||||
|
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + ownerUserId));
|
||||||
|
Pet pet = new Pet();
|
||||||
|
pet.setOwner(owner);
|
||||||
|
pet.setStore(null);
|
||||||
|
pet.setPetStatus("Owned");
|
||||||
|
applyMyPetRequest(pet, request);
|
||||||
|
return mapToMyPetResponse(petRepository.save(pet));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public MyPetResponse updateMyPet(Long ownerUserId, Long petId, MyPetRequest request) {
|
||||||
|
Pet pet = findOwnedPet(ownerUserId, petId);
|
||||||
|
pet.setPetStatus("Owned");
|
||||||
|
pet.setStore(null);
|
||||||
|
applyMyPetRequest(pet, request);
|
||||||
|
return mapToMyPetResponse(petRepository.save(pet));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void deleteMyPet(Long ownerUserId, Long petId) {
|
||||||
|
Pet pet = findOwnedPet(ownerUserId, petId);
|
||||||
|
deleteStoredImageIfPresent(pet.getImageUrl());
|
||||||
|
petRepository.delete(pet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public MyPetResponse uploadMyPetImage(Long ownerUserId, Long petId, MultipartFile file) throws IOException {
|
||||||
|
validateImageFile(file);
|
||||||
|
Pet pet = findOwnedPet(ownerUserId, petId);
|
||||||
|
deleteStoredImageIfPresent(pet.getImageUrl());
|
||||||
|
pet.setImageUrl(catalogImageStorageService.storePetImage(file));
|
||||||
|
return mapToMyPetResponse(petRepository.save(pet));
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public PetResponse createPet(PetRequest request) {
|
public PetResponse createPet(PetRequest request) {
|
||||||
Pet pet = new Pet();
|
Pet pet = new Pet();
|
||||||
@@ -225,6 +272,11 @@ public class PetService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Pet findOwnedPet(Long ownerUserId, Long petId) {
|
||||||
|
return petRepository.findByIdAndOwner_Id(petId, ownerUserId)
|
||||||
|
.orElseThrow(() -> new ResourceNotFoundException("Pet not found with id: " + petId));
|
||||||
|
}
|
||||||
|
|
||||||
private void deleteStoredImageIfPresent(String storedImagePath) {
|
private void deleteStoredImageIfPresent(String storedImagePath) {
|
||||||
if (storedImagePath == null || storedImagePath.isBlank()) {
|
if (storedImagePath == null || storedImagePath.isBlank()) {
|
||||||
return;
|
return;
|
||||||
@@ -276,6 +328,32 @@ public class PetService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MyPetResponse mapToMyPetResponse(Pet pet) {
|
||||||
|
return new MyPetResponse(
|
||||||
|
pet.getPetId(),
|
||||||
|
pet.getPetName(),
|
||||||
|
pet.getPetSpecies(),
|
||||||
|
pet.getPetBreed(),
|
||||||
|
pet.getImageUrl() != null && !pet.getImageUrl().isBlank() ? "/api/v1/pets/" + pet.getPetId() + "/image" : null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyMyPetRequest(Pet pet, MyPetRequest request) {
|
||||||
|
pet.setPetName(request.getPetName().trim());
|
||||||
|
pet.setPetSpecies(request.getSpecies().trim());
|
||||||
|
pet.setPetBreed(normalizeOptional(request.getBreed()));
|
||||||
|
pet.setPetAge(null);
|
||||||
|
pet.setPetPrice(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizeOptional(String value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String trimmed = value.trim();
|
||||||
|
return trimmed.isEmpty() ? null : trimmed;
|
||||||
|
}
|
||||||
|
|
||||||
private void applyOwnerAndStore(Pet pet, PetRequest request) {
|
private void applyOwnerAndStore(Pet pet, PetRequest request) {
|
||||||
if ("owned".equalsIgnoreCase(request.getPetStatus())) {
|
if ("owned".equalsIgnoreCase(request.getPetStatus())) {
|
||||||
if (request.getCustomerId() != null) {
|
if (request.getCustomerId() != null) {
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import com.petshop.backend.entity.Product;
|
|||||||
import com.petshop.backend.entity.Refund;
|
import com.petshop.backend.entity.Refund;
|
||||||
import com.petshop.backend.entity.RefundItem;
|
import com.petshop.backend.entity.RefundItem;
|
||||||
import com.petshop.backend.entity.Sale;
|
import com.petshop.backend.entity.Sale;
|
||||||
|
import com.petshop.backend.exception.BusinessException;
|
||||||
|
import com.petshop.backend.exception.ResourceNotFoundException;
|
||||||
import com.petshop.backend.repository.ProductRepository;
|
import com.petshop.backend.repository.ProductRepository;
|
||||||
import com.petshop.backend.repository.RefundRepository;
|
import com.petshop.backend.repository.RefundRepository;
|
||||||
import com.petshop.backend.repository.SaleRepository;
|
import com.petshop.backend.repository.SaleRepository;
|
||||||
@@ -40,14 +42,14 @@ public class RefundService {
|
|||||||
@Transactional
|
@Transactional
|
||||||
public RefundResponse createRefund(RefundRequest request, Long customerId) {
|
public RefundResponse createRefund(RefundRequest request, Long customerId) {
|
||||||
Sale sale = saleRepository.findById(request.getSaleId())
|
Sale sale = saleRepository.findById(request.getSaleId())
|
||||||
.orElseThrow(() -> new RuntimeException("Sale not found"));
|
.orElseThrow(() -> new BusinessException("Sale not found"));
|
||||||
|
|
||||||
if (sale.getCustomer() == null) {
|
if (sale.getCustomer() == null) {
|
||||||
throw new RuntimeException("Sale has no associated customer");
|
throw new BusinessException("Sale has no associated customer");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (customerId != null && !sale.getCustomer().getId().equals(customerId)) {
|
if (customerId != null && !sale.getCustomer().getId().equals(customerId)) {
|
||||||
throw new RuntimeException("You can only create refunds for your own purchases");
|
throw new BusinessException("You can only create refunds for your own purchases");
|
||||||
}
|
}
|
||||||
|
|
||||||
Refund refund = new Refund();
|
Refund refund = new Refund();
|
||||||
@@ -59,13 +61,13 @@ public class RefundService {
|
|||||||
BigDecimal totalAmount = BigDecimal.ZERO;
|
BigDecimal totalAmount = BigDecimal.ZERO;
|
||||||
for (var itemRequest : request.getItems()) {
|
for (var itemRequest : request.getItems()) {
|
||||||
Product product = productRepository.findById(itemRequest.getProdId())
|
Product product = productRepository.findById(itemRequest.getProdId())
|
||||||
.orElseThrow(() -> new RuntimeException("Product not found: " + itemRequest.getProdId()));
|
.orElseThrow(() -> new BusinessException("Product not found: " + itemRequest.getProdId()));
|
||||||
|
|
||||||
BigDecimal unitPrice = sale.getItems().stream()
|
BigDecimal unitPrice = sale.getItems().stream()
|
||||||
.filter(item -> item.getProduct().getProdId().equals(itemRequest.getProdId()))
|
.filter(item -> item.getProduct().getProdId().equals(itemRequest.getProdId()))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.map(item -> item.getUnitPrice())
|
.map(item -> item.getUnitPrice())
|
||||||
.orElseThrow(() -> new RuntimeException("Product " + itemRequest.getProdId() + " was not in original sale"));
|
.orElseThrow(() -> new BusinessException("Product " + itemRequest.getProdId() + " was not in original sale"));
|
||||||
|
|
||||||
RefundItem refundItem = new RefundItem();
|
RefundItem refundItem = new RefundItem();
|
||||||
refundItem.setProduct(product);
|
refundItem.setProduct(product);
|
||||||
@@ -84,10 +86,10 @@ public class RefundService {
|
|||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public RefundResponse getRefundById(Long id, Long customerId) {
|
public RefundResponse getRefundById(Long id, Long customerId) {
|
||||||
Refund refund = refundRepository.findById(id)
|
Refund refund = refundRepository.findById(id)
|
||||||
.orElseThrow(() -> new RuntimeException("Refund not found"));
|
.orElseThrow(() -> new ResourceNotFoundException("Refund not found"));
|
||||||
|
|
||||||
if (customerId != null && !refund.getCustomerId().equals(customerId)) {
|
if (customerId != null && !refund.getCustomerId().equals(customerId)) {
|
||||||
throw new RuntimeException("You can only view your own refunds");
|
throw new ResourceNotFoundException("You can only view your own refunds");
|
||||||
}
|
}
|
||||||
|
|
||||||
return toResponse(refund);
|
return toResponse(refund);
|
||||||
@@ -111,18 +113,18 @@ public class RefundService {
|
|||||||
@Transactional
|
@Transactional
|
||||||
public RefundResponse updateRefundStatus(Long id, String status) {
|
public RefundResponse updateRefundStatus(Long id, String status) {
|
||||||
Refund refund = refundRepository.findById(id)
|
Refund refund = refundRepository.findById(id)
|
||||||
.orElseThrow(() -> new RuntimeException("Refund not found"));
|
.orElseThrow(() -> new ResourceNotFoundException("Refund not found"));
|
||||||
|
|
||||||
Refund.RefundStatus newStatus;
|
Refund.RefundStatus newStatus;
|
||||||
try {
|
try {
|
||||||
newStatus = Refund.RefundStatus.valueOf(status.toUpperCase());
|
newStatus = Refund.RefundStatus.valueOf(status.toUpperCase());
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw new RuntimeException("Invalid status: " + status);
|
throw new BusinessException("Invalid status: " + status);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (refund.getStatus() == Refund.RefundStatus.PENDING && newStatus == Refund.RefundStatus.APPROVED) {
|
if (refund.getStatus() == Refund.RefundStatus.PENDING && newStatus == Refund.RefundStatus.APPROVED) {
|
||||||
Sale originalSale = saleRepository.findById(refund.getSaleId())
|
Sale originalSale = saleRepository.findById(refund.getSaleId())
|
||||||
.orElseThrow(() -> new RuntimeException("Original sale not found"));
|
.orElseThrow(() -> new ResourceNotFoundException("Original sale not found"));
|
||||||
|
|
||||||
SaleRequest saleRequest = new SaleRequest();
|
SaleRequest saleRequest = new SaleRequest();
|
||||||
saleRequest.setStoreId(originalSale.getStore().getStoreId());
|
saleRequest.setStoreId(originalSale.getStore().getStoreId());
|
||||||
@@ -150,7 +152,7 @@ public class RefundService {
|
|||||||
@Transactional
|
@Transactional
|
||||||
public void deleteRefund(Long id) {
|
public void deleteRefund(Long id) {
|
||||||
if (!refundRepository.existsById(id)) {
|
if (!refundRepository.existsById(id)) {
|
||||||
throw new RuntimeException("Refund not found");
|
throw new ResourceNotFoundException("Refund not found");
|
||||||
}
|
}
|
||||||
refundRepository.deleteById(id);
|
refundRepository.deleteById(id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ public class AdoptionRequest {
|
|||||||
private Long petId;
|
private Long petId;
|
||||||
private Long customerId;
|
private Long customerId;
|
||||||
private Long employeeId;
|
private Long employeeId;
|
||||||
|
private Long sourceStoreId;
|
||||||
private LocalDate adoptionDate;
|
private LocalDate adoptionDate;
|
||||||
private String adoptionStatus;
|
private String adoptionStatus;
|
||||||
|
|
||||||
@@ -36,6 +37,14 @@ public class AdoptionRequest {
|
|||||||
this.employeeId = employeeId;
|
this.employeeId = employeeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getSourceStoreId() {
|
||||||
|
return sourceStoreId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSourceStoreId(Long sourceStoreId) {
|
||||||
|
this.sourceStoreId = sourceStoreId;
|
||||||
|
}
|
||||||
|
|
||||||
public LocalDate getAdoptionDate() {
|
public LocalDate getAdoptionDate() {
|
||||||
return adoptionDate;
|
return adoptionDate;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,8 @@ package org.example.petshopdesktop.api.dto.appointment;
|
|||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class AppointmentRequest {
|
public class AppointmentRequest {
|
||||||
private List<Long> petIds;
|
|
||||||
private List<Long> customerPetIds;
|
|
||||||
private Long customerId;
|
private Long customerId;
|
||||||
private Long storeId;
|
private Long storeId;
|
||||||
private Long serviceId;
|
private Long serviceId;
|
||||||
@@ -14,26 +11,11 @@ public class AppointmentRequest {
|
|||||||
private LocalDate appointmentDate;
|
private LocalDate appointmentDate;
|
||||||
private LocalTime appointmentTime;
|
private LocalTime appointmentTime;
|
||||||
private String appointmentStatus;
|
private String appointmentStatus;
|
||||||
|
private Long petId;
|
||||||
|
|
||||||
public AppointmentRequest() {
|
public AppointmentRequest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Long> getPetIds() {
|
|
||||||
return petIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPetIds(List<Long> petIds) {
|
|
||||||
this.petIds = petIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Long> getCustomerPetIds() {
|
|
||||||
return customerPetIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCustomerPetIds(List<Long> customerPetIds) {
|
|
||||||
this.customerPetIds = customerPetIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getCustomerId() {
|
public Long getCustomerId() {
|
||||||
return customerId;
|
return customerId;
|
||||||
}
|
}
|
||||||
@@ -89,4 +71,12 @@ public class AppointmentRequest {
|
|||||||
public void setAppointmentStatus(String appointmentStatus) {
|
public void setAppointmentStatus(String appointmentStatus) {
|
||||||
this.appointmentStatus = appointmentStatus;
|
this.appointmentStatus = appointmentStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getPetId() {
|
||||||
|
return petId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPetId(Long petId) {
|
||||||
|
this.petId = petId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,16 +10,14 @@ public class AppointmentResponse {
|
|||||||
private Long storeId;
|
private Long storeId;
|
||||||
private String storeName;
|
private String storeName;
|
||||||
private Long serviceId;
|
private Long serviceId;
|
||||||
private java.util.List<String> petNames;
|
|
||||||
private java.util.List<Long> petIds;
|
|
||||||
private java.util.List<String> customerPetNames;
|
|
||||||
private java.util.List<Long> customerPetIds;
|
|
||||||
private String serviceName;
|
private String serviceName;
|
||||||
private Long employeeId;
|
private Long employeeId;
|
||||||
private String employeeName;
|
private String employeeName;
|
||||||
private LocalDate appointmentDate;
|
private LocalDate appointmentDate;
|
||||||
private LocalTime appointmentTime;
|
private LocalTime appointmentTime;
|
||||||
private String appointmentStatus;
|
private String appointmentStatus;
|
||||||
|
private String petName;
|
||||||
|
private Long petId;
|
||||||
|
|
||||||
public AppointmentResponse() {
|
public AppointmentResponse() {
|
||||||
}
|
}
|
||||||
@@ -72,36 +70,20 @@ public class AppointmentResponse {
|
|||||||
this.serviceId = serviceId;
|
this.serviceId = serviceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public java.util.List<String> getPetNames() {
|
public String getPetName() {
|
||||||
return petNames;
|
return petName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPetNames(java.util.List<String> petNames) {
|
public void setPetName(String petName) {
|
||||||
this.petNames = petNames;
|
this.petName = petName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public java.util.List<Long> getPetIds() {
|
public Long getPetId() {
|
||||||
return petIds;
|
return petId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPetIds(java.util.List<Long> petIds) {
|
public void setPetId(Long petId) {
|
||||||
this.petIds = petIds;
|
this.petId = petId;
|
||||||
}
|
|
||||||
|
|
||||||
public java.util.List<String> getCustomerPetNames() {
|
|
||||||
return customerPetNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCustomerPetNames(java.util.List<String> customerPetNames) {
|
|
||||||
this.customerPetNames = customerPetNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
public java.util.List<Long> getCustomerPetIds() {
|
|
||||||
return customerPetIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCustomerPetIds(java.util.List<Long> customerPetIds) {
|
|
||||||
this.customerPetIds = customerPetIds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getServiceName() {
|
public String getServiceName() {
|
||||||
|
|||||||
@@ -2,6 +2,10 @@ package org.example.petshopdesktop.api.dto.chat;
|
|||||||
|
|
||||||
public class MessageRequest {
|
public class MessageRequest {
|
||||||
private String content;
|
private String content;
|
||||||
|
private String attachmentUrl;
|
||||||
|
private String attachmentName;
|
||||||
|
private String attachmentMimeType;
|
||||||
|
private Long attachmentSizeBytes;
|
||||||
|
|
||||||
public MessageRequest() {
|
public MessageRequest() {
|
||||||
}
|
}
|
||||||
@@ -17,4 +21,36 @@ public class MessageRequest {
|
|||||||
public void setContent(String content) {
|
public void setContent(String content) {
|
||||||
this.content = content;
|
this.content = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getAttachmentUrl() {
|
||||||
|
return attachmentUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttachmentUrl(String attachmentUrl) {
|
||||||
|
this.attachmentUrl = attachmentUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAttachmentName() {
|
||||||
|
return attachmentName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttachmentName(String attachmentName) {
|
||||||
|
this.attachmentName = attachmentName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAttachmentMimeType() {
|
||||||
|
return attachmentMimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttachmentMimeType(String attachmentMimeType) {
|
||||||
|
this.attachmentMimeType = attachmentMimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getAttachmentSizeBytes() {
|
||||||
|
return attachmentSizeBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttachmentSizeBytes(Long attachmentSizeBytes) {
|
||||||
|
this.attachmentSizeBytes = attachmentSizeBytes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ public class MessageResponse {
|
|||||||
private String content;
|
private String content;
|
||||||
private LocalDateTime timestamp;
|
private LocalDateTime timestamp;
|
||||||
private Boolean isRead;
|
private Boolean isRead;
|
||||||
|
private String attachmentUrl;
|
||||||
|
private String attachmentName;
|
||||||
|
private String attachmentMimeType;
|
||||||
|
private Long attachmentSizeBytes;
|
||||||
|
|
||||||
public MessageResponse() {
|
public MessageResponse() {
|
||||||
}
|
}
|
||||||
@@ -60,4 +64,36 @@ public class MessageResponse {
|
|||||||
public void setIsRead(Boolean isRead) {
|
public void setIsRead(Boolean isRead) {
|
||||||
this.isRead = isRead;
|
this.isRead = isRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getAttachmentUrl() {
|
||||||
|
return attachmentUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttachmentUrl(String attachmentUrl) {
|
||||||
|
this.attachmentUrl = attachmentUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAttachmentName() {
|
||||||
|
return attachmentName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttachmentName(String attachmentName) {
|
||||||
|
this.attachmentName = attachmentName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAttachmentMimeType() {
|
||||||
|
return attachmentMimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttachmentMimeType(String attachmentMimeType) {
|
||||||
|
this.attachmentMimeType = attachmentMimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getAttachmentSizeBytes() {
|
||||||
|
return attachmentSizeBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttachmentSizeBytes(Long attachmentSizeBytes) {
|
||||||
|
this.attachmentSizeBytes = attachmentSizeBytes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,12 @@ public class EmployeeRequest {
|
|||||||
private String password;
|
private String password;
|
||||||
private String firstName;
|
private String firstName;
|
||||||
private String lastName;
|
private String lastName;
|
||||||
|
private String fullName;
|
||||||
private String email;
|
private String email;
|
||||||
private String phone;
|
private String phone;
|
||||||
private String role;
|
private String role;
|
||||||
|
private String staffRole;
|
||||||
|
private Long primaryStoreId;
|
||||||
private Boolean active;
|
private Boolean active;
|
||||||
|
|
||||||
public String getUsername() { return username; }
|
public String getUsername() { return username; }
|
||||||
@@ -18,12 +21,18 @@ public class EmployeeRequest {
|
|||||||
public void setFirstName(String firstName) { this.firstName = firstName; }
|
public void setFirstName(String firstName) { this.firstName = firstName; }
|
||||||
public String getLastName() { return lastName; }
|
public String getLastName() { return lastName; }
|
||||||
public void setLastName(String lastName) { this.lastName = lastName; }
|
public void setLastName(String lastName) { this.lastName = lastName; }
|
||||||
|
public String getFullName() { return fullName; }
|
||||||
|
public void setFullName(String fullName) { this.fullName = fullName; }
|
||||||
public String getEmail() { return email; }
|
public String getEmail() { return email; }
|
||||||
public void setEmail(String email) { this.email = email; }
|
public void setEmail(String email) { this.email = email; }
|
||||||
public String getPhone() { return phone; }
|
public String getPhone() { return phone; }
|
||||||
public void setPhone(String phone) { this.phone = phone; }
|
public void setPhone(String phone) { this.phone = phone; }
|
||||||
public String getRole() { return role; }
|
public String getRole() { return role; }
|
||||||
public void setRole(String role) { this.role = role; }
|
public void setRole(String role) { this.role = role; }
|
||||||
|
public String getStaffRole() { return staffRole; }
|
||||||
|
public void setStaffRole(String staffRole) { this.staffRole = staffRole; }
|
||||||
|
public Long getPrimaryStoreId() { return primaryStoreId; }
|
||||||
|
public void setPrimaryStoreId(Long primaryStoreId) { this.primaryStoreId = primaryStoreId; }
|
||||||
public Boolean getActive() { return active; }
|
public Boolean getActive() { return active; }
|
||||||
public void setActive(Boolean active) { this.active = active; }
|
public void setActive(Boolean active) { this.active = active; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.example.petshopdesktop.api.dto.employee;
|
|||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
public class EmployeeResponse {
|
public class EmployeeResponse {
|
||||||
|
private Long id;
|
||||||
private Long employeeId;
|
private Long employeeId;
|
||||||
private Long userId;
|
private Long userId;
|
||||||
private String username;
|
private String username;
|
||||||
@@ -12,13 +13,17 @@ public class EmployeeResponse {
|
|||||||
private String email;
|
private String email;
|
||||||
private String phone;
|
private String phone;
|
||||||
private String role;
|
private String role;
|
||||||
|
private String staffRole;
|
||||||
|
private Long primaryStoreId;
|
||||||
private Boolean active;
|
private Boolean active;
|
||||||
private LocalDateTime createdAt;
|
private LocalDateTime createdAt;
|
||||||
private LocalDateTime updatedAt;
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
public Long getEmployeeId() { return employeeId; }
|
public Long getId() { return id; }
|
||||||
|
public void setId(Long id) { this.id = id; }
|
||||||
|
public Long getEmployeeId() { return employeeId != null ? employeeId : id; }
|
||||||
public void setEmployeeId(Long employeeId) { this.employeeId = employeeId; }
|
public void setEmployeeId(Long employeeId) { this.employeeId = employeeId; }
|
||||||
public Long getUserId() { return userId; }
|
public Long getUserId() { return userId != null ? userId : id; }
|
||||||
public void setUserId(Long userId) { this.userId = userId; }
|
public void setUserId(Long userId) { this.userId = userId; }
|
||||||
public String getUsername() { return username; }
|
public String getUsername() { return username; }
|
||||||
public void setUsername(String username) { this.username = username; }
|
public void setUsername(String username) { this.username = username; }
|
||||||
@@ -34,6 +39,10 @@ public class EmployeeResponse {
|
|||||||
public void setPhone(String phone) { this.phone = phone; }
|
public void setPhone(String phone) { this.phone = phone; }
|
||||||
public String getRole() { return role; }
|
public String getRole() { return role; }
|
||||||
public void setRole(String role) { this.role = role; }
|
public void setRole(String role) { this.role = role; }
|
||||||
|
public String getStaffRole() { return staffRole; }
|
||||||
|
public void setStaffRole(String staffRole) { this.staffRole = staffRole; }
|
||||||
|
public Long getPrimaryStoreId() { return primaryStoreId; }
|
||||||
|
public void setPrimaryStoreId(Long primaryStoreId) { this.primaryStoreId = primaryStoreId; }
|
||||||
public Boolean getActive() { return active; }
|
public Boolean getActive() { return active; }
|
||||||
public void setActive(Boolean active) { this.active = active; }
|
public void setActive(Boolean active) { this.active = active; }
|
||||||
public LocalDateTime getCreatedAt() { return createdAt; }
|
public LocalDateTime getCreatedAt() { return createdAt; }
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.example.petshopdesktop.api.dto.inventory;
|
|||||||
public class InventoryRequest {
|
public class InventoryRequest {
|
||||||
private Long prodId;
|
private Long prodId;
|
||||||
private Integer quantity;
|
private Integer quantity;
|
||||||
|
private Long storeId;
|
||||||
|
|
||||||
public InventoryRequest() {
|
public InventoryRequest() {
|
||||||
}
|
}
|
||||||
@@ -22,4 +23,12 @@ public class InventoryRequest {
|
|||||||
public void setQuantity(Integer quantity) {
|
public void setQuantity(Integer quantity) {
|
||||||
this.quantity = quantity;
|
this.quantity = quantity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getStoreId() {
|
||||||
|
return storeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStoreId(Long storeId) {
|
||||||
|
this.storeId = storeId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ public class InventoryResponse {
|
|||||||
private Long prodId;
|
private Long prodId;
|
||||||
private String productName;
|
private String productName;
|
||||||
private String categoryName;
|
private String categoryName;
|
||||||
|
private Long storeId;
|
||||||
|
private String storeName;
|
||||||
private Integer quantity;
|
private Integer quantity;
|
||||||
private LocalDateTime createdAt;
|
private LocalDateTime createdAt;
|
||||||
private LocalDateTime updatedAt;
|
private LocalDateTime updatedAt;
|
||||||
@@ -46,6 +48,22 @@ public class InventoryResponse {
|
|||||||
this.categoryName = categoryName;
|
this.categoryName = categoryName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getStoreId() {
|
||||||
|
return storeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStoreId(Long storeId) {
|
||||||
|
this.storeId = storeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStoreName() {
|
||||||
|
return storeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStoreName(String storeName) {
|
||||||
|
this.storeName = storeName;
|
||||||
|
}
|
||||||
|
|
||||||
public Integer getQuantity() {
|
public Integer getQuantity() {
|
||||||
return quantity;
|
return quantity;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -231,18 +231,12 @@ public class AppointmentController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private AppointmentDTO mapToAppointmentDTO(AppointmentResponse response) {
|
private AppointmentDTO mapToAppointmentDTO(AppointmentResponse response) {
|
||||||
Long petId = response.getCustomerPetIds() != null && !response.getCustomerPetIds().isEmpty()
|
|
||||||
? response.getCustomerPetIds().get(0)
|
|
||||||
: response.getPetIds() != null && !response.getPetIds().isEmpty() ? response.getPetIds().get(0) : null;
|
|
||||||
String petName = response.getCustomerPetNames() != null && !response.getCustomerPetNames().isEmpty()
|
|
||||||
? String.join(", ", response.getCustomerPetNames())
|
|
||||||
: String.join(", ", response.getPetNames());
|
|
||||||
return new AppointmentDTO(
|
return new AppointmentDTO(
|
||||||
response.getAppointmentId().intValue(),
|
response.getAppointmentId().intValue(),
|
||||||
response.getCustomerId() != null ? response.getCustomerId().intValue() : 0,
|
response.getCustomerId() != null ? response.getCustomerId().intValue() : 0,
|
||||||
response.getCustomerName(),
|
response.getCustomerName(),
|
||||||
petId != null ? petId.intValue() : 0,
|
response.getPetId() != null ? response.getPetId().intValue() : 0,
|
||||||
petName,
|
response.getPetName(),
|
||||||
response.getServiceId() != null ? response.getServiceId().intValue() : 0,
|
response.getServiceId() != null ? response.getServiceId().intValue() : 0,
|
||||||
response.getServiceName(),
|
response.getServiceName(),
|
||||||
response.getEmployeeId() != null ? response.getEmployeeId().intValue() : 0,
|
response.getEmployeeId() != null ? response.getEmployeeId().intValue() : 0,
|
||||||
|
|||||||
@@ -123,20 +123,28 @@ public class ChatController {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void btnSendClicked() {
|
void btnSendClicked() {
|
||||||
if (selectedConversation == null) {
|
try {
|
||||||
lblChatStatus.setText("Select a conversation");
|
if (selectedConversation == null) {
|
||||||
return;
|
lblChatStatus.setText("Select a conversation");
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
String content = txtMessage.getText() == null ? "" : txtMessage.getText().trim();
|
String content = txtMessage.getText() == null ? "" : txtMessage.getText().trim();
|
||||||
if (content.isEmpty()) {
|
if (content.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
txtMessage.clear();
|
txtMessage.clear();
|
||||||
boolean sent = realtimeClient.sendMessage(selectedConversation.getId(), content);
|
boolean sent = realtimeClient.sendMessage(selectedConversation.getId(), content);
|
||||||
if (!sent) {
|
if (!sent) {
|
||||||
sendMessageFallback(selectedConversation.getId(), content);
|
sendMessageFallback(selectedConversation.getId(), content);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
ActivityLogger.getInstance().logException(
|
||||||
|
"ChatController.btnSendClicked",
|
||||||
|
e,
|
||||||
|
"Sending chat message");
|
||||||
|
lblChatStatus.setText("Chat send failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,16 +231,30 @@ public class ChatController {
|
|||||||
private void renderMessages(List<MessageResponse> messages) {
|
private void renderMessages(List<MessageResponse> messages) {
|
||||||
vbMessages.getChildren().clear();
|
vbMessages.getChildren().clear();
|
||||||
for (MessageResponse message : messages) {
|
for (MessageResponse message : messages) {
|
||||||
vbMessages.getChildren().add(createMessageBubble(message));
|
try {
|
||||||
|
vbMessages.getChildren().add(createMessageBubble(message));
|
||||||
|
} catch (Exception e) {
|
||||||
|
ActivityLogger.getInstance().logException(
|
||||||
|
"ChatController.renderMessages",
|
||||||
|
e,
|
||||||
|
"Rendering chat message");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
scrollMessagesToBottom();
|
scrollMessagesToBottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendMessageIfSelected(MessageResponse message) {
|
private void appendMessageIfSelected(MessageResponse message) {
|
||||||
upsertConversationForMessage(message);
|
try {
|
||||||
if (selectedConversation != null && selectedConversation.getId().equals(message.getConversationId())) {
|
upsertConversationForMessage(message);
|
||||||
vbMessages.getChildren().add(createMessageBubble(message));
|
if (selectedConversation != null && selectedConversation.getId().equals(message.getConversationId())) {
|
||||||
scrollMessagesToBottom();
|
vbMessages.getChildren().add(createMessageBubble(message));
|
||||||
|
scrollMessagesToBottom();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
ActivityLogger.getInstance().logException(
|
||||||
|
"ChatController.appendMessageIfSelected",
|
||||||
|
e,
|
||||||
|
"Appending chat message");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,15 +306,34 @@ public class ChatController {
|
|||||||
Label author = new Label(resolveAuthorLabel(message));
|
Label author = new Label(resolveAuthorLabel(message));
|
||||||
author.setStyle("-fx-font-weight: bold; -fx-text-fill: " + (mine ? "#ffffff" : "#1f2937") + ";");
|
author.setStyle("-fx-font-weight: bold; -fx-text-fill: " + (mine ? "#ffffff" : "#1f2937") + ";");
|
||||||
|
|
||||||
Label content = new Label(message.getContent());
|
|
||||||
content.setWrapText(true);
|
|
||||||
content.setStyle("-fx-text-fill: " + (mine ? "#ffffff" : "#1f2937") + ";");
|
|
||||||
|
|
||||||
String timestampText = message.getTimestamp() == null ? "" : TIME_FORMATTER.format(message.getTimestamp());
|
String timestampText = message.getTimestamp() == null ? "" : TIME_FORMATTER.format(message.getTimestamp());
|
||||||
Label timestamp = new Label(timestampText);
|
Label timestamp = new Label(timestampText);
|
||||||
timestamp.setStyle("-fx-text-fill: " + (mine ? "#dbeafe" : "#94a3b8") + "; -fx-font-size: 11px;");
|
timestamp.setStyle("-fx-text-fill: " + (mine ? "#dbeafe" : "#94a3b8") + "; -fx-font-size: 11px;");
|
||||||
|
|
||||||
VBox bubble = new VBox(4, author, content, timestamp);
|
VBox bubble = new VBox(4, author);
|
||||||
|
String contentText = message.getContent() == null ? "" : message.getContent();
|
||||||
|
if (!contentText.isBlank()) {
|
||||||
|
Label content = new Label(contentText);
|
||||||
|
content.setWrapText(true);
|
||||||
|
content.setStyle("-fx-text-fill: " + (mine ? "#ffffff" : "#1f2937") + ";");
|
||||||
|
bubble.getChildren().add(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.getAttachmentUrl() != null && !message.getAttachmentUrl().isBlank()) {
|
||||||
|
String attachmentLabel = message.getAttachmentName();
|
||||||
|
if (attachmentLabel == null || attachmentLabel.isBlank()) {
|
||||||
|
attachmentLabel = "Attachment";
|
||||||
|
}
|
||||||
|
if (message.getAttachmentSizeBytes() != null && message.getAttachmentSizeBytes() > 0) {
|
||||||
|
attachmentLabel = attachmentLabel + " (" + formatSize(message.getAttachmentSizeBytes()) + ")";
|
||||||
|
}
|
||||||
|
Label attachment = new Label(attachmentLabel);
|
||||||
|
attachment.setWrapText(true);
|
||||||
|
attachment.setStyle("-fx-text-fill: " + (mine ? "#dbeafe" : "#0f766e") + "; -fx-underline: true;");
|
||||||
|
bubble.getChildren().add(attachment);
|
||||||
|
}
|
||||||
|
|
||||||
|
bubble.getChildren().add(timestamp);
|
||||||
bubble.setMaxWidth(420);
|
bubble.setMaxWidth(420);
|
||||||
bubble.setStyle(mine
|
bubble.setStyle(mine
|
||||||
? "-fx-background-color: #0f766e; -fx-background-radius: 14; -fx-padding: 12;"
|
? "-fx-background-color: #0f766e; -fx-background-radius: 14; -fx-padding: 12;"
|
||||||
@@ -347,4 +388,17 @@ public class ChatController {
|
|||||||
private void scrollMessagesToBottom() {
|
private void scrollMessagesToBottom() {
|
||||||
Platform.runLater(() -> spMessages.setVvalue(1.0));
|
Platform.runLater(() -> spMessages.setVvalue(1.0));
|
||||||
}
|
}
|
||||||
|
private String formatSize(Long bytes) {
|
||||||
|
if (bytes == null || bytes <= 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
double size = bytes;
|
||||||
|
String[] units = {"B", "KB", "MB", "GB"};
|
||||||
|
int unitIndex = 0;
|
||||||
|
while (size >= 1024 && unitIndex < units.length - 1) {
|
||||||
|
size = size / 1024;
|
||||||
|
unitIndex++;
|
||||||
|
}
|
||||||
|
return unitIndex == 0 ? String.format("%.0f %s", size, units[unitIndex]) : String.format("%.1f %s", size, units[unitIndex]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -235,8 +235,8 @@ public class InventoryController {
|
|||||||
response.getProdId() != null ? response.getProdId().intValue() : 0,
|
response.getProdId() != null ? response.getProdId().intValue() : 0,
|
||||||
response.getProductName(),
|
response.getProductName(),
|
||||||
response.getCategoryName() != null ? response.getCategoryName() : "",
|
response.getCategoryName() != null ? response.getCategoryName() : "",
|
||||||
0,
|
response.getStoreId() != null ? response.getStoreId().intValue() : 0,
|
||||||
"N/A",
|
response.getStoreName() != null ? response.getStoreName() : "",
|
||||||
response.getQuantity() != null ? response.getQuantity() : 0,
|
response.getQuantity() != null ? response.getQuantity() : 0,
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -186,10 +186,14 @@ public class StaffAccountsController {
|
|||||||
long userId = employee.getUserId() != null ? employee.getUserId() : 0L;
|
long userId = employee.getUserId() != null ? employee.getUserId() : 0L;
|
||||||
long employeeId = employee.getEmployeeId() != null ? employee.getEmployeeId() : 0L;
|
long employeeId = employee.getEmployeeId() != null ? employee.getEmployeeId() : 0L;
|
||||||
String username = employee.getUsername();
|
String username = employee.getUsername();
|
||||||
String fullName = employee.getFullName() != null ? employee.getFullName() : "";
|
String firstName = employee.getFirstName() != null ? employee.getFirstName() : "";
|
||||||
String[] names = splitFullName(fullName);
|
String lastName = employee.getLastName() != null ? employee.getLastName() : "";
|
||||||
String firstName = names[0];
|
if (firstName.isBlank() && lastName.isBlank()) {
|
||||||
String lastName = names[1];
|
String fullName = employee.getFullName() != null ? employee.getFullName() : "";
|
||||||
|
String[] names = splitFullName(fullName);
|
||||||
|
firstName = names[0];
|
||||||
|
lastName = names[1];
|
||||||
|
}
|
||||||
String email = employee.getEmail() != null ? employee.getEmail() : "";
|
String email = employee.getEmail() != null ? employee.getEmail() : "";
|
||||||
String phone = employee.getPhone() != null ? employee.getPhone() : "";
|
String phone = employee.getPhone() != null ? employee.getPhone() : "";
|
||||||
String role = employee.getRole() != null ? employee.getRole() : "STAFF";
|
String role = employee.getRole() != null ? employee.getRole() : "STAFF";
|
||||||
|
|||||||
@@ -190,10 +190,16 @@ public class AdoptionDialogController {
|
|||||||
|
|
||||||
if (errorMsg.isEmpty()) {
|
if (errorMsg.isEmpty()) {
|
||||||
try {
|
try {
|
||||||
|
Long storeId = UserSession.getInstance().getStoreId();
|
||||||
|
if (storeId == null || storeId <= 0) {
|
||||||
|
throw new IllegalStateException("Store is not set for this account");
|
||||||
|
}
|
||||||
|
|
||||||
AdoptionRequest request = new AdoptionRequest();
|
AdoptionRequest request = new AdoptionRequest();
|
||||||
request.setPetId(cbPet.getSelectionModel().getSelectedItem().getId());
|
request.setPetId(cbPet.getSelectionModel().getSelectedItem().getId());
|
||||||
request.setCustomerId(cbCustomer.getSelectionModel().getSelectedItem().getId());
|
request.setCustomerId(cbCustomer.getSelectionModel().getSelectedItem().getId());
|
||||||
request.setEmployeeId(cbEmployee.getSelectionModel().getSelectedItem().getId());
|
request.setEmployeeId(cbEmployee.getSelectionModel().getSelectedItem().getId());
|
||||||
|
request.setSourceStoreId(storeId);
|
||||||
request.setAdoptionDate(dpAdoptionDate.getValue());
|
request.setAdoptionDate(dpAdoptionDate.getValue());
|
||||||
request.setAdoptionStatus(cbAdoptionStatus.getValue());
|
request.setAdoptionStatus(cbAdoptionStatus.getValue());
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import org.example.petshopdesktop.util.ActivityLogger;
|
|||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class AppointmentDialogController {
|
public class AppointmentDialogController {
|
||||||
@@ -215,7 +214,7 @@ public class AppointmentDialogController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AppointmentRequest request = new AppointmentRequest();
|
AppointmentRequest request = new AppointmentRequest();
|
||||||
request.setCustomerPetIds(Collections.singletonList(cbPet.getValue().getId()));
|
request.setPetId(cbPet.getValue().getId());
|
||||||
request.setCustomerId(cbCustomer.getValue().getId());
|
request.setCustomerId(cbCustomer.getValue().getId());
|
||||||
request.setStoreId(storeId);
|
request.setStoreId(storeId);
|
||||||
request.setServiceId(cbService.getValue().getId());
|
request.setServiceId(cbService.getValue().getId());
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import org.example.petshopdesktop.api.dto.inventory.InventoryResponse;
|
|||||||
import org.example.petshopdesktop.api.dto.product.ProductResponse;
|
import org.example.petshopdesktop.api.dto.product.ProductResponse;
|
||||||
import org.example.petshopdesktop.api.endpoints.InventoryApi;
|
import org.example.petshopdesktop.api.endpoints.InventoryApi;
|
||||||
import org.example.petshopdesktop.api.endpoints.ProductApi;
|
import org.example.petshopdesktop.api.endpoints.ProductApi;
|
||||||
|
import org.example.petshopdesktop.auth.UserSession;
|
||||||
import org.example.petshopdesktop.models.Product;
|
import org.example.petshopdesktop.models.Product;
|
||||||
import org.example.petshopdesktop.util.ActivityLogger;
|
import org.example.petshopdesktop.util.ActivityLogger;
|
||||||
|
|
||||||
@@ -127,6 +128,10 @@ public class InventoryDialogController {
|
|||||||
try {
|
try {
|
||||||
InventoryRequest request = new InventoryRequest();
|
InventoryRequest request = new InventoryRequest();
|
||||||
Product selectedProduct = cbProduct.getSelectionModel().getSelectedItem();
|
Product selectedProduct = cbProduct.getSelectionModel().getSelectedItem();
|
||||||
|
Long storeId = UserSession.getInstance().getStoreId();
|
||||||
|
if (storeId == null || storeId <= 0) {
|
||||||
|
throw new IllegalStateException("Store is not set for this account");
|
||||||
|
}
|
||||||
request.setProdId((long) selectedProduct.getProdId());
|
request.setProdId((long) selectedProduct.getProdId());
|
||||||
int quantity;
|
int quantity;
|
||||||
try {
|
try {
|
||||||
@@ -135,6 +140,7 @@ public class InventoryDialogController {
|
|||||||
throw new IllegalArgumentException("Invalid quantity format");
|
throw new IllegalArgumentException("Invalid quantity format");
|
||||||
}
|
}
|
||||||
request.setQuantity(quantity);
|
request.setQuantity(quantity);
|
||||||
|
request.setStoreId(storeId);
|
||||||
|
|
||||||
if (mode.equals("Add")) {
|
if (mode.equals("Add")) {
|
||||||
InventoryApi.getInstance().createInventory(request);
|
InventoryApi.getInstance().createInventory(request);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import javafx.stage.Stage;
|
|||||||
import org.example.petshopdesktop.Validator;
|
import org.example.petshopdesktop.Validator;
|
||||||
import org.example.petshopdesktop.api.dto.employee.EmployeeRequest;
|
import org.example.petshopdesktop.api.dto.employee.EmployeeRequest;
|
||||||
import org.example.petshopdesktop.api.endpoints.EmployeeApi;
|
import org.example.petshopdesktop.api.endpoints.EmployeeApi;
|
||||||
|
import org.example.petshopdesktop.auth.UserSession;
|
||||||
import org.example.petshopdesktop.models.StaffAccount;
|
import org.example.petshopdesktop.models.StaffAccount;
|
||||||
import org.example.petshopdesktop.util.ActivityLogger;
|
import org.example.petshopdesktop.util.ActivityLogger;
|
||||||
|
|
||||||
@@ -104,14 +105,18 @@ public class StaffEditDialogController {
|
|||||||
|
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
|
Long storeId = UserSession.getInstance().getStoreId();
|
||||||
EmployeeRequest request = new EmployeeRequest();
|
EmployeeRequest request = new EmployeeRequest();
|
||||||
request.setUsername(username);
|
request.setUsername(username);
|
||||||
request.setPassword(password.isEmpty() ? null : password);
|
request.setPassword(password.isEmpty() ? null : password);
|
||||||
request.setFirstName(firstName);
|
request.setFirstName(firstName);
|
||||||
request.setLastName(lastName);
|
request.setLastName(lastName);
|
||||||
|
request.setFullName(firstName + " " + lastName);
|
||||||
request.setEmail(email);
|
request.setEmail(email);
|
||||||
request.setPhone(phone);
|
request.setPhone(phone);
|
||||||
request.setRole(staffAccount.getRole());
|
request.setRole(staffAccount.getRole());
|
||||||
|
request.setStaffRole("Staff");
|
||||||
|
request.setPrimaryStoreId(storeId);
|
||||||
request.setActive(staffAccount.isActive());
|
request.setActive(staffAccount.isActive());
|
||||||
|
|
||||||
EmployeeApi.getInstance().updateEmployee(staffAccount.getEmployeeId(), request);
|
EmployeeApi.getInstance().updateEmployee(staffAccount.getEmployeeId(), request);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import javafx.scene.control.TextField;
|
|||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import org.example.petshopdesktop.api.dto.employee.EmployeeRequest;
|
import org.example.petshopdesktop.api.dto.employee.EmployeeRequest;
|
||||||
import org.example.petshopdesktop.api.endpoints.EmployeeApi;
|
import org.example.petshopdesktop.api.endpoints.EmployeeApi;
|
||||||
|
import org.example.petshopdesktop.auth.UserSession;
|
||||||
import org.example.petshopdesktop.Validator;
|
import org.example.petshopdesktop.Validator;
|
||||||
import org.example.petshopdesktop.util.ActivityLogger;
|
import org.example.petshopdesktop.util.ActivityLogger;
|
||||||
|
|
||||||
@@ -89,14 +90,18 @@ public class StaffRegisterDialogController {
|
|||||||
|
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
|
Long storeId = UserSession.getInstance().getStoreId();
|
||||||
EmployeeRequest request = new EmployeeRequest();
|
EmployeeRequest request = new EmployeeRequest();
|
||||||
request.setUsername(username);
|
request.setUsername(username);
|
||||||
request.setPassword(password);
|
request.setPassword(password);
|
||||||
request.setFirstName(firstName);
|
request.setFirstName(firstName);
|
||||||
request.setLastName(lastName);
|
request.setLastName(lastName);
|
||||||
|
request.setFullName(firstName + " " + lastName);
|
||||||
request.setEmail(email);
|
request.setEmail(email);
|
||||||
request.setPhone(phone);
|
request.setPhone(phone);
|
||||||
request.setRole("STAFF");
|
request.setRole("STAFF");
|
||||||
|
request.setStaffRole("Staff");
|
||||||
|
request.setPrimaryStoreId(storeId);
|
||||||
request.setActive(true);
|
request.setActive(true);
|
||||||
|
|
||||||
EmployeeApi.getInstance().createEmployee(request);
|
EmployeeApi.getInstance().createEmployee(request);
|
||||||
|
|||||||
114
web/package-lock.json
generated
114
web/package-lock.json
generated
@@ -8,7 +8,7 @@
|
|||||||
"name": "threaded-pets",
|
"name": "threaded-pets",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"next": "16.1.6",
|
"next": "^16.2.2",
|
||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
"react-dom": "19.2.3"
|
"react-dom": "19.2.3"
|
||||||
},
|
},
|
||||||
@@ -1032,9 +1032,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/env": {
|
"node_modules/@next/env": {
|
||||||
"version": "16.1.6",
|
"version": "16.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.2.tgz",
|
||||||
"integrity": "sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ==",
|
"integrity": "sha512-LqSGz5+xGk9EL/iBDr2yo/CgNQV6cFsNhRR2xhSXYh7B/hb4nePCxlmDvGEKG30NMHDFf0raqSyOZiQrO7BkHQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@next/eslint-plugin-next": {
|
"node_modules/@next/eslint-plugin-next": {
|
||||||
@@ -1048,9 +1048,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-darwin-arm64": {
|
"node_modules/@next/swc-darwin-arm64": {
|
||||||
"version": "16.1.6",
|
"version": "16.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.2.tgz",
|
||||||
"integrity": "sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw==",
|
"integrity": "sha512-B92G3ulrwmkDSEJEp9+XzGLex5wC1knrmCSIylyVeiAtCIfvEJYiN3v5kXPlYt5R4RFlsfO/v++aKV63Acrugg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -1064,9 +1064,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-darwin-x64": {
|
"node_modules/@next/swc-darwin-x64": {
|
||||||
"version": "16.1.6",
|
"version": "16.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.2.tgz",
|
||||||
"integrity": "sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ==",
|
"integrity": "sha512-7ZwSgNKJNQiwW0CKhNm9B1WS2L1Olc4B2XY0hPYCAL3epFnugMhuw5TMWzMilQ3QCZcCHoYm9NGWTHbr5REFxw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -1080,9 +1080,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-linux-arm64-gnu": {
|
"node_modules/@next/swc-linux-arm64-gnu": {
|
||||||
"version": "16.1.6",
|
"version": "16.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.2.tgz",
|
||||||
"integrity": "sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw==",
|
"integrity": "sha512-c3m8kBHMziMgo2fICOP/cd/5YlrxDU5YYjAJeQLyFsCqVF8xjOTH/QYG4a2u48CvvZZSj1eHQfBCbyh7kBr30Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -1096,9 +1096,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-linux-arm64-musl": {
|
"node_modules/@next/swc-linux-arm64-musl": {
|
||||||
"version": "16.1.6",
|
"version": "16.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.2.tgz",
|
||||||
"integrity": "sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==",
|
"integrity": "sha512-VKLuscm0P/mIfzt+SDdn2+8TNNJ7f0qfEkA+az7OqQbjzKdBxAHs0UvuiVoCtbwX+dqMEL9U54b5wQ/aN3dHeg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -1112,9 +1112,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-linux-x64-gnu": {
|
"node_modules/@next/swc-linux-x64-gnu": {
|
||||||
"version": "16.1.6",
|
"version": "16.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.2.tgz",
|
||||||
"integrity": "sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==",
|
"integrity": "sha512-kU3OPHJq6sBUjOk7wc5zJ7/lipn8yGldMoAv4z67j6ov6Xo/JvzA7L7LCsyzzsXmgLEhk3Qkpwqaq/1+XpNR3g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -1128,9 +1128,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-linux-x64-musl": {
|
"node_modules/@next/swc-linux-x64-musl": {
|
||||||
"version": "16.1.6",
|
"version": "16.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.2.tgz",
|
||||||
"integrity": "sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==",
|
"integrity": "sha512-CKXRILyErMtUftp+coGcZ38ZwE/Aqq45VMCcRLr2I4OXKrgxIBDXHnBgeX/UMil0S09i2JXaDL3Q+TN8D/cKmg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -1144,9 +1144,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-win32-arm64-msvc": {
|
"node_modules/@next/swc-win32-arm64-msvc": {
|
||||||
"version": "16.1.6",
|
"version": "16.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.2.tgz",
|
||||||
"integrity": "sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==",
|
"integrity": "sha512-sS/jSk5VUoShUqINJFvNjVT7JfR5ORYj/+/ZpOYbbIohv/lQfduWnGAycq2wlknbOql2xOR0DoV0s6Xfcy49+g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -1160,9 +1160,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-win32-x64-msvc": {
|
"node_modules/@next/swc-win32-x64-msvc": {
|
||||||
"version": "16.1.6",
|
"version": "16.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.2.tgz",
|
||||||
"integrity": "sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A==",
|
"integrity": "sha512-aHaKceJgdySReT7qeck5oShucxWRiiEuwCGK8HHALe6yZga8uyFpLkPgaRw3kkF04U7ROogL/suYCNt/+CuXGA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -1741,9 +1741,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
|
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
|
||||||
"version": "5.0.4",
|
"version": "5.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
|
||||||
"integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
|
"integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -2422,9 +2422,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "1.1.12",
|
"version": "1.1.13",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz",
|
||||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
"integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -3568,9 +3568,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/flatted": {
|
"node_modules/flatted": {
|
||||||
"version": "3.3.4",
|
"version": "3.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
|
||||||
"integrity": "sha512-3+mMldrTAPdta5kjX2G2J7iX4zxtnwpdA8Tr2ZSjkyPSanvbZAcy6flmtnXbEybHrDcU9641lxrMfFuUxVz9vA==",
|
"integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
@@ -4954,14 +4954,14 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/next": {
|
"node_modules/next": {
|
||||||
"version": "16.1.6",
|
"version": "16.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/next/-/next-16.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/next/-/next-16.2.2.tgz",
|
||||||
"integrity": "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw==",
|
"integrity": "sha512-i6AJdyVa4oQjyvX/6GeER8dpY/xlIV+4NMv/svykcLtURJSy/WzDnnUk/TM4d0uewFHK7xSQz4TbIwPgjky+3A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@next/env": "16.1.6",
|
"@next/env": "16.2.2",
|
||||||
"@swc/helpers": "0.5.15",
|
"@swc/helpers": "0.5.15",
|
||||||
"baseline-browser-mapping": "^2.8.3",
|
"baseline-browser-mapping": "^2.9.19",
|
||||||
"caniuse-lite": "^1.0.30001579",
|
"caniuse-lite": "^1.0.30001579",
|
||||||
"postcss": "8.4.31",
|
"postcss": "8.4.31",
|
||||||
"styled-jsx": "5.1.6"
|
"styled-jsx": "5.1.6"
|
||||||
@@ -4973,15 +4973,15 @@
|
|||||||
"node": ">=20.9.0"
|
"node": ">=20.9.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@next/swc-darwin-arm64": "16.1.6",
|
"@next/swc-darwin-arm64": "16.2.2",
|
||||||
"@next/swc-darwin-x64": "16.1.6",
|
"@next/swc-darwin-x64": "16.2.2",
|
||||||
"@next/swc-linux-arm64-gnu": "16.1.6",
|
"@next/swc-linux-arm64-gnu": "16.2.2",
|
||||||
"@next/swc-linux-arm64-musl": "16.1.6",
|
"@next/swc-linux-arm64-musl": "16.2.2",
|
||||||
"@next/swc-linux-x64-gnu": "16.1.6",
|
"@next/swc-linux-x64-gnu": "16.2.2",
|
||||||
"@next/swc-linux-x64-musl": "16.1.6",
|
"@next/swc-linux-x64-musl": "16.2.2",
|
||||||
"@next/swc-win32-arm64-msvc": "16.1.6",
|
"@next/swc-win32-arm64-msvc": "16.2.2",
|
||||||
"@next/swc-win32-x64-msvc": "16.1.6",
|
"@next/swc-win32-x64-msvc": "16.2.2",
|
||||||
"sharp": "^0.34.4"
|
"sharp": "^0.34.5"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@opentelemetry/api": "^1.1.0",
|
"@opentelemetry/api": "^1.1.0",
|
||||||
@@ -5298,9 +5298,9 @@
|
|||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/picomatch": {
|
"node_modules/picomatch": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
|
||||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -6099,9 +6099,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tinyglobby/node_modules/picomatch": {
|
"node_modules/tinyglobby/node_modules/picomatch": {
|
||||||
"version": "4.0.3",
|
"version": "4.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
"lint": "eslint"
|
"lint": "eslint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"next": "16.1.6",
|
"next": "^16.2.2",
|
||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
"react-dom": "19.2.3"
|
"react-dom": "19.2.3"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user