diff --git a/.gitignore b/.gitignore
index bc434064..54f866f6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,5 +2,4 @@
.local/
commit-patches/
temp_photos/
-uploads/
.env
diff --git a/backend/.gitignore b/backend/.gitignore
index 242ba1f0..0989bc79 100644
--- a/backend/.gitignore
+++ b/backend/.gitignore
@@ -46,7 +46,10 @@ build/
### Project Specific ###
src/test/
tmp/
-uploads/
+uploads/*
+!uploads/avatars/
+!uploads/pets/
+!uploads/products/
### Temp and backup files ###
*.backup
diff --git a/backend/Dockerfile b/backend/Dockerfile
index 08a06773..c3479c8e 100644
--- a/backend/Dockerfile
+++ b/backend/Dockerfile
@@ -9,5 +9,6 @@ RUN mvn -q -DskipTests package
FROM eclipse-temurin:25-jre
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
+COPY uploads ./uploads
EXPOSE 8080
-ENTRYPOINT ["java","-jar","app.jar"]
\ No newline at end of file
+ENTRYPOINT ["java","-jar","app.jar"]
diff --git a/backend/pom.xml b/backend/pom.xml
index 9f2157b4..206ae451 100644
--- a/backend/pom.xml
+++ b/backend/pom.xml
@@ -151,6 +151,12 @@
com.petshop.backend.DevStackApplication
runtime
+
+
+ UPLOAD_BASE_DIR
+ ${project.basedir}/uploads
+
+
diff --git a/backend/src/main/java/com/petshop/backend/controller/AuthController.java b/backend/src/main/java/com/petshop/backend/controller/AuthController.java
index c0eff22e..33281560 100644
--- a/backend/src/main/java/com/petshop/backend/controller/AuthController.java
+++ b/backend/src/main/java/com/petshop/backend/controller/AuthController.java
@@ -1,11 +1,15 @@
package com.petshop.backend.controller;
import com.petshop.backend.dto.auth.AvatarUploadResponse;
+import com.petshop.backend.dto.auth.ForgotPasswordRequest;
+import com.petshop.backend.dto.auth.ForgotPasswordResponse;
import com.petshop.backend.dto.auth.LoginRequest;
import com.petshop.backend.dto.auth.LoginResponse;
import com.petshop.backend.dto.auth.ProfileUpdateRequest;
import com.petshop.backend.dto.auth.RegisterRequest;
import com.petshop.backend.dto.auth.RegisterResponse;
+import com.petshop.backend.dto.auth.ResetPasswordRequest;
+import com.petshop.backend.dto.auth.ResetPasswordResponse;
import com.petshop.backend.dto.auth.UserInfoResponse;
import com.petshop.backend.entity.StoreLocation;
import com.petshop.backend.entity.User;
@@ -13,6 +17,7 @@ import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.security.JwtUtil;
import com.petshop.backend.service.ActivityLogService;
import com.petshop.backend.service.AvatarStorageService;
+import com.petshop.backend.service.PasswordResetService;
import com.petshop.backend.util.AuthenticationHelper;
import com.petshop.backend.util.PhoneUtils;
import jakarta.validation.Valid;
@@ -49,14 +54,16 @@ public class AuthController {
private final PasswordEncoder passwordEncoder;
private final AvatarStorageService avatarStorageService;
private final ActivityLogService activityLogService;
+ private final PasswordResetService passwordResetService;
- public AuthController(AuthenticationManager authenticationManager, UserRepository userRepository, JwtUtil jwtUtil, PasswordEncoder passwordEncoder, AvatarStorageService avatarStorageService, ActivityLogService activityLogService) {
+ public AuthController(AuthenticationManager authenticationManager, UserRepository userRepository, JwtUtil jwtUtil, PasswordEncoder passwordEncoder, AvatarStorageService avatarStorageService, ActivityLogService activityLogService, PasswordResetService passwordResetService) {
this.authenticationManager = authenticationManager;
this.userRepository = userRepository;
this.jwtUtil = jwtUtil;
this.passwordEncoder = passwordEncoder;
this.avatarStorageService = avatarStorageService;
this.activityLogService = activityLogService;
+ this.passwordResetService = passwordResetService;
}
@PostMapping("/register")
@@ -153,6 +160,16 @@ public class AuthController {
}
}
+ @PostMapping("/forgot-password")
+ public ResponseEntity forgotPassword(@Valid @RequestBody ForgotPasswordRequest request) {
+ return ResponseEntity.ok(passwordResetService.createResetToken(request.getUsernameOrEmail()));
+ }
+
+ @PostMapping("/reset-password")
+ public ResponseEntity resetPassword(@Valid @RequestBody ResetPasswordRequest request) {
+ return ResponseEntity.ok(passwordResetService.resetPassword(request.getToken(), request.getNewPassword()));
+ }
+
@Transactional(readOnly = true)
@GetMapping("/me")
public ResponseEntity getCurrentUser() {
diff --git a/backend/src/main/java/com/petshop/backend/dto/auth/ForgotPasswordRequest.java b/backend/src/main/java/com/petshop/backend/dto/auth/ForgotPasswordRequest.java
new file mode 100644
index 00000000..529581cc
--- /dev/null
+++ b/backend/src/main/java/com/petshop/backend/dto/auth/ForgotPasswordRequest.java
@@ -0,0 +1,17 @@
+package com.petshop.backend.dto.auth;
+
+import jakarta.validation.constraints.NotBlank;
+
+public class ForgotPasswordRequest {
+
+ @NotBlank(message = "Username or email is required")
+ private String usernameOrEmail;
+
+ public String getUsernameOrEmail() {
+ return usernameOrEmail;
+ }
+
+ public void setUsernameOrEmail(String usernameOrEmail) {
+ this.usernameOrEmail = usernameOrEmail;
+ }
+}
diff --git a/backend/src/main/java/com/petshop/backend/dto/auth/ForgotPasswordResponse.java b/backend/src/main/java/com/petshop/backend/dto/auth/ForgotPasswordResponse.java
new file mode 100644
index 00000000..6062a196
--- /dev/null
+++ b/backend/src/main/java/com/petshop/backend/dto/auth/ForgotPasswordResponse.java
@@ -0,0 +1,20 @@
+package com.petshop.backend.dto.auth;
+
+public class ForgotPasswordResponse {
+
+ private final String message;
+ private final String resetToken;
+
+ public ForgotPasswordResponse(String message, String resetToken) {
+ this.message = message;
+ this.resetToken = resetToken;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public String getResetToken() {
+ return resetToken;
+ }
+}
diff --git a/backend/src/main/java/com/petshop/backend/dto/auth/ResetPasswordRequest.java b/backend/src/main/java/com/petshop/backend/dto/auth/ResetPasswordRequest.java
new file mode 100644
index 00000000..3f20f7f7
--- /dev/null
+++ b/backend/src/main/java/com/petshop/backend/dto/auth/ResetPasswordRequest.java
@@ -0,0 +1,30 @@
+package com.petshop.backend.dto.auth;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+
+public class ResetPasswordRequest {
+
+ @NotBlank(message = "Reset token is required")
+ private String token;
+
+ @NotBlank(message = "Password is required")
+ @Size(min = 6, message = "Password must be at least 6 characters")
+ private String newPassword;
+
+ public String getToken() {
+ return token;
+ }
+
+ public void setToken(String token) {
+ this.token = token;
+ }
+
+ public String getNewPassword() {
+ return newPassword;
+ }
+
+ public void setNewPassword(String newPassword) {
+ this.newPassword = newPassword;
+ }
+}
diff --git a/backend/src/main/java/com/petshop/backend/dto/auth/ResetPasswordResponse.java b/backend/src/main/java/com/petshop/backend/dto/auth/ResetPasswordResponse.java
new file mode 100644
index 00000000..0edf18a7
--- /dev/null
+++ b/backend/src/main/java/com/petshop/backend/dto/auth/ResetPasswordResponse.java
@@ -0,0 +1,14 @@
+package com.petshop.backend.dto.auth;
+
+public class ResetPasswordResponse {
+
+ private final String message;
+
+ public ResetPasswordResponse(String message) {
+ this.message = message;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+}
diff --git a/backend/src/main/java/com/petshop/backend/dto/cart/CartResponse.java b/backend/src/main/java/com/petshop/backend/dto/cart/CartResponse.java
index 4e2442f6..d5fc9faf 100644
--- a/backend/src/main/java/com/petshop/backend/dto/cart/CartResponse.java
+++ b/backend/src/main/java/com/petshop/backend/dto/cart/CartResponse.java
@@ -11,8 +11,11 @@ public class CartResponse {
private List items;
private BigDecimal subtotalAmount;
private BigDecimal discountAmount;
+ private BigDecimal pointsDiscountAmount;
private BigDecimal totalAmount;
private String couponCode;
+ private Boolean pointsApplied;
+ private Integer availableLoyaltyPoints;
public CartResponse() {
}
@@ -40,4 +43,14 @@ public class CartResponse {
public String getCouponCode() { return couponCode; }
public void setCouponCode(String couponCode) { this.couponCode = couponCode; }
+
+ public BigDecimal getPointsDiscountAmount() { return pointsDiscountAmount; }
+ public void setPointsDiscountAmount(BigDecimal pointsDiscountAmount) { this.pointsDiscountAmount = pointsDiscountAmount; }
+
+ public Boolean getPointsApplied() { return pointsApplied; }
+ public void setPointsApplied(Boolean pointsApplied) { this.pointsApplied = pointsApplied; }
+
+ public Integer getAvailableLoyaltyPoints() { return availableLoyaltyPoints; }
+ public void setAvailableLoyaltyPoints(Integer availableLoyaltyPoints) { this.availableLoyaltyPoints = availableLoyaltyPoints; }
+
}
diff --git a/backend/src/main/java/com/petshop/backend/dto/sale/SaleRequest.java b/backend/src/main/java/com/petshop/backend/dto/sale/SaleRequest.java
index 97248688..9faba27f 100644
--- a/backend/src/main/java/com/petshop/backend/dto/sale/SaleRequest.java
+++ b/backend/src/main/java/com/petshop/backend/dto/sale/SaleRequest.java
@@ -5,7 +5,6 @@ import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.List;
import java.util.Objects;
-import java.math.BigDecimal;
public class SaleRequest {
@NotNull(message = "Store ID is required")
@@ -29,9 +28,6 @@ public class SaleRequest {
private Long cartId;
- private Integer pointsUsed;
-
- private BigDecimal pointsDiscountAmount;
public Long getStoreId() {
return storeId;
@@ -105,21 +101,6 @@ public class SaleRequest {
this.cartId = cartId;
}
- public Integer getPointsUsed() {
- return pointsUsed;
- }
-
- public void setPointsUsed(Integer pointsUsed) {
- this.pointsUsed = pointsUsed;
- }
-
- public BigDecimal getPointsDiscountAmount() {
- return pointsDiscountAmount;
- }
-
- public void setPointsDiscountAmount(BigDecimal pointsDiscountAmount) {
- this.pointsDiscountAmount = pointsDiscountAmount;
- }
@Override
public boolean equals(Object o) {
diff --git a/backend/src/main/java/com/petshop/backend/dto/sale/SaleResponse.java b/backend/src/main/java/com/petshop/backend/dto/sale/SaleResponse.java
index eaebd4e8..8f2b4fe5 100644
--- a/backend/src/main/java/com/petshop/backend/dto/sale/SaleResponse.java
+++ b/backend/src/main/java/com/petshop/backend/dto/sale/SaleResponse.java
@@ -18,9 +18,8 @@ public class SaleResponse {
private BigDecimal subtotalAmount;
private BigDecimal couponDiscountAmount;
private BigDecimal employeeDiscountAmount;
+ private BigDecimal loyaltyDiscountAmount;
private Integer pointsEarned;
- private Integer pointsUsed;
- private BigDecimal pointsDiscountAmount;
private String channel;
private Long couponId;
private Long cartId;
@@ -129,6 +128,15 @@ public class SaleResponse {
this.employeeDiscountAmount = employeeDiscountAmount;
}
+ public BigDecimal getLoyaltyDiscountAmount() {
+ return loyaltyDiscountAmount;
+ }
+
+ public void setLoyaltyDiscountAmount(BigDecimal loyaltyDiscountAmount) {
+ this.loyaltyDiscountAmount = loyaltyDiscountAmount;
+ }
+
+
public Integer getPointsEarned() {
return pointsEarned;
}
@@ -137,22 +145,6 @@ public class SaleResponse {
this.pointsEarned = pointsEarned;
}
- public Integer getPointsUsed() {
- return pointsUsed;
- }
-
- public void setPointsUsed(Integer pointsUsed) {
- this.pointsUsed = pointsUsed;
- }
-
- public BigDecimal getPointsDiscountAmount() {
- return pointsDiscountAmount;
- }
-
- public void setPointsDiscountAmount(BigDecimal pointsDiscountAmount) {
- this.pointsDiscountAmount = pointsDiscountAmount;
- }
-
public String getChannel() {
return channel;
}
diff --git a/backend/src/main/java/com/petshop/backend/entity/ActivityLog.java b/backend/src/main/java/com/petshop/backend/entity/ActivityLog.java
index 04dc79ec..b445b7c4 100644
--- a/backend/src/main/java/com/petshop/backend/entity/ActivityLog.java
+++ b/backend/src/main/java/com/petshop/backend/entity/ActivityLog.java
@@ -1,11 +1,13 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
+import org.hibernate.annotations.Immutable;
import java.time.LocalDateTime;
import java.util.Objects;
@Entity
+@Immutable
@Table(name = "activityLog")
public class ActivityLog {
diff --git a/backend/src/main/java/com/petshop/backend/entity/Cart.java b/backend/src/main/java/com/petshop/backend/entity/Cart.java
index ba0566f8..61066af6 100644
--- a/backend/src/main/java/com/petshop/backend/entity/Cart.java
+++ b/backend/src/main/java/com/petshop/backend/entity/Cart.java
@@ -40,6 +40,24 @@ public class Cart {
@Column(nullable = false, precision = 10, scale = 2)
private BigDecimal totalAmount = BigDecimal.ZERO;
+ @Column(nullable = false)
+ private Boolean pointsApplied = false;
+
+ @Column(nullable = false, precision = 10, scale = 2)
+ private BigDecimal pointsDiscountAmount = BigDecimal.ZERO;
+
+ @Column(nullable = false)
+ private Boolean checkoutPending = false;
+
+ @Column(precision = 10, scale = 2)
+ private BigDecimal checkoutAmount;
+
+ @Column
+ private LocalDateTime checkoutStartedAt;
+
+ @Column(length = 255)
+ private String checkoutPaymentIntentId;
+
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@@ -115,6 +133,54 @@ public class Cart {
this.totalAmount = totalAmount;
}
+ public Boolean getPointsApplied() {
+ return pointsApplied;
+ }
+
+ public void setPointsApplied(Boolean pointsApplied) {
+ this.pointsApplied = pointsApplied;
+ }
+
+ public BigDecimal getPointsDiscountAmount() {
+ return pointsDiscountAmount;
+ }
+
+ public void setPointsDiscountAmount(BigDecimal pointsDiscountAmount) {
+ this.pointsDiscountAmount = pointsDiscountAmount;
+ }
+
+ public Boolean getCheckoutPending() {
+ return checkoutPending;
+ }
+
+ public void setCheckoutPending(Boolean checkoutPending) {
+ this.checkoutPending = checkoutPending;
+ }
+
+ public BigDecimal getCheckoutAmount() {
+ return checkoutAmount;
+ }
+
+ public void setCheckoutAmount(BigDecimal checkoutAmount) {
+ this.checkoutAmount = checkoutAmount;
+ }
+
+ public LocalDateTime getCheckoutStartedAt() {
+ return checkoutStartedAt;
+ }
+
+ public void setCheckoutStartedAt(LocalDateTime checkoutStartedAt) {
+ this.checkoutStartedAt = checkoutStartedAt;
+ }
+
+ public String getCheckoutPaymentIntentId() {
+ return checkoutPaymentIntentId;
+ }
+
+ public void setCheckoutPaymentIntentId(String checkoutPaymentIntentId) {
+ this.checkoutPaymentIntentId = checkoutPaymentIntentId;
+ }
+
public LocalDateTime getCreatedAt() {
return createdAt;
}
diff --git a/backend/src/main/java/com/petshop/backend/entity/PasswordResetToken.java b/backend/src/main/java/com/petshop/backend/entity/PasswordResetToken.java
new file mode 100644
index 00000000..bd6c5494
--- /dev/null
+++ b/backend/src/main/java/com/petshop/backend/entity/PasswordResetToken.java
@@ -0,0 +1,102 @@
+package com.petshop.backend.entity;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.Table;
+import org.hibernate.annotations.CreationTimestamp;
+
+import java.time.LocalDateTime;
+import java.util.Objects;
+
+@Entity
+@Table(name = "passwordResetToken")
+public class PasswordResetToken {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "userId", nullable = false)
+ private User user;
+
+ @Column(nullable = false, unique = true, length = 64)
+ private String tokenHash;
+
+ @Column(nullable = false)
+ private LocalDateTime expiresAt;
+
+ @Column
+ private LocalDateTime usedAt;
+
+ @CreationTimestamp
+ @Column(name = "created_at", updatable = false)
+ private LocalDateTime createdAt;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public User getUser() {
+ return user;
+ }
+
+ public void setUser(User user) {
+ this.user = user;
+ }
+
+ public String getTokenHash() {
+ return tokenHash;
+ }
+
+ public void setTokenHash(String tokenHash) {
+ this.tokenHash = tokenHash;
+ }
+
+ public LocalDateTime getExpiresAt() {
+ return expiresAt;
+ }
+
+ public void setExpiresAt(LocalDateTime expiresAt) {
+ this.expiresAt = expiresAt;
+ }
+
+ public LocalDateTime getUsedAt() {
+ return usedAt;
+ }
+
+ public void setUsedAt(LocalDateTime usedAt) {
+ this.usedAt = usedAt;
+ }
+
+ public LocalDateTime getCreatedAt() {
+ return createdAt;
+ }
+
+ public void setCreatedAt(LocalDateTime createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PasswordResetToken that = (PasswordResetToken) o;
+ return Objects.equals(id, that.id);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id);
+ }
+}
diff --git a/backend/src/main/java/com/petshop/backend/entity/Sale.java b/backend/src/main/java/com/petshop/backend/entity/Sale.java
index f994a855..e61204fd 100644
--- a/backend/src/main/java/com/petshop/backend/entity/Sale.java
+++ b/backend/src/main/java/com/petshop/backend/entity/Sale.java
@@ -66,14 +66,12 @@ public class Sale {
@Column(nullable = false, precision = 10, scale = 2)
private BigDecimal employeeDiscountAmount = BigDecimal.ZERO;
- @Column(nullable = false)
- private Integer pointsEarned = 0;
-
- @Column(nullable = false)
- private Integer pointsUsed = 0;
@Column(nullable = false, precision = 10, scale = 2)
- private BigDecimal pointsDiscountAmount = BigDecimal.ZERO;
+ private BigDecimal loyaltyDiscountAmount = BigDecimal.ZERO;
+
+ @Column(nullable = false)
+ private Integer pointsEarned = 0;
@OneToMany(mappedBy = "sale", cascade = CascadeType.ALL)
private List items = new ArrayList<>();
@@ -209,6 +207,15 @@ public class Sale {
this.employeeDiscountAmount = employeeDiscountAmount;
}
+
+ public BigDecimal getLoyaltyDiscountAmount() {
+ return loyaltyDiscountAmount;
+ }
+
+ public void setLoyaltyDiscountAmount(BigDecimal loyaltyDiscountAmount) {
+ this.loyaltyDiscountAmount = loyaltyDiscountAmount;
+ }
+
public Integer getPointsEarned() {
return pointsEarned;
}
@@ -217,22 +224,6 @@ public class Sale {
this.pointsEarned = pointsEarned;
}
- public Integer getPointsUsed() {
- return pointsUsed;
- }
-
- public void setPointsUsed(Integer pointsUsed) {
- this.pointsUsed = pointsUsed;
- }
-
- public BigDecimal getPointsDiscountAmount() {
- return pointsDiscountAmount;
- }
-
- public void setPointsDiscountAmount(BigDecimal pointsDiscountAmount) {
- this.pointsDiscountAmount = pointsDiscountAmount;
- }
-
public List getItems() {
return items;
}
diff --git a/backend/src/main/java/com/petshop/backend/repository/PasswordResetTokenRepository.java b/backend/src/main/java/com/petshop/backend/repository/PasswordResetTokenRepository.java
new file mode 100644
index 00000000..2d34d381
--- /dev/null
+++ b/backend/src/main/java/com/petshop/backend/repository/PasswordResetTokenRepository.java
@@ -0,0 +1,17 @@
+package com.petshop.backend.repository;
+
+import com.petshop.backend.entity.PasswordResetToken;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Optional;
+
+@Repository
+public interface PasswordResetTokenRepository extends JpaRepository {
+
+ List findByUser_IdAndUsedAtIsNull(Long userId);
+
+ Optional findByTokenHashAndUsedAtIsNullAndExpiresAtAfter(String tokenHash, LocalDateTime now);
+}
diff --git a/backend/src/main/java/com/petshop/backend/repository/SaleRepository.java b/backend/src/main/java/com/petshop/backend/repository/SaleRepository.java
index c648886e..5a7798c2 100644
--- a/backend/src/main/java/com/petshop/backend/repository/SaleRepository.java
+++ b/backend/src/main/java/com/petshop/backend/repository/SaleRepository.java
@@ -9,6 +9,7 @@ import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
+import java.util.Optional;
@Repository
public interface SaleRepository extends JpaRepository {
@@ -25,4 +26,6 @@ public interface SaleRepository extends JpaRepository {
Page searchSales(@Param("q") String query, @Param("paymentMethod") String paymentMethod, @Param("storeId") Long storeId, @Param("isRefund") Boolean isRefund, Pageable pageable);
List findByOriginalSaleSaleId(Long originalSaleId);
+
+ Optional findByCartCartId(Long cartId);
}
diff --git a/backend/src/main/java/com/petshop/backend/security/SecurityConfig.java b/backend/src/main/java/com/petshop/backend/security/SecurityConfig.java
index 1a6cedce..b15d4a96 100644
--- a/backend/src/main/java/com/petshop/backend/security/SecurityConfig.java
+++ b/backend/src/main/java/com/petshop/backend/security/SecurityConfig.java
@@ -50,7 +50,12 @@ public class SecurityConfig {
http.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
- .requestMatchers("/api/v1/auth/login", "/api/v1/auth/register").permitAll()
+ .requestMatchers(
+ "/api/v1/auth/login",
+ "/api/v1/auth/register",
+ "/api/v1/auth/forgot-password",
+ "/api/v1/auth/reset-password"
+ ).permitAll()
.requestMatchers("/api/v1/health").permitAll()
.requestMatchers("/ws/chat/**", "/ws/chat-sockjs/**").permitAll()
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/swagger-ui.html").permitAll()
diff --git a/backend/src/main/java/com/petshop/backend/service/AvatarStorageService.java b/backend/src/main/java/com/petshop/backend/service/AvatarStorageService.java
index dff64508..2229c3d4 100644
--- a/backend/src/main/java/com/petshop/backend/service/AvatarStorageService.java
+++ b/backend/src/main/java/com/petshop/backend/service/AvatarStorageService.java
@@ -1,6 +1,7 @@
package com.petshop.backend.service;
import com.petshop.backend.entity.User;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.PathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
@@ -8,6 +9,7 @@ import org.springframework.http.MediaTypeFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
+import jakarta.annotation.PostConstruct;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -22,7 +24,15 @@ public class AvatarStorageService {
private static final String STORED_PREFIX = "/uploads/avatars/";
private static final String OWNER_ENDPOINT = "/api/v1/auth/me/avatar/file";
- private final Path avatarDirectory = Paths.get("uploads", "avatars").toAbsolutePath().normalize();
+ @Value("${app.upload.base-dir:uploads}")
+ private String uploadBaseDir;
+
+ private Path avatarDirectory;
+
+ @PostConstruct
+ private void init() {
+ avatarDirectory = Paths.get(uploadBaseDir, "avatars").toAbsolutePath().normalize();
+ }
public String storeAvatar(MultipartFile file) throws IOException {
Files.createDirectories(avatarDirectory);
@@ -101,7 +111,7 @@ public class AvatarStorageService {
}
String extension = originalFilename.substring(extensionIndex).toLowerCase(Locale.ROOT);
return switch (extension) {
- case ".jpg", ".jpeg", ".png", ".gif" -> extension;
+ case ".jpg", ".jpeg", ".png", ".gif", ".webp" -> extension;
default -> ".jpg";
};
}
diff --git a/backend/src/main/java/com/petshop/backend/service/CartService.java b/backend/src/main/java/com/petshop/backend/service/CartService.java
index d406a44d..1714f518 100644
--- a/backend/src/main/java/com/petshop/backend/service/CartService.java
+++ b/backend/src/main/java/com/petshop/backend/service/CartService.java
@@ -1,6 +1,8 @@
package com.petshop.backend.service;
import com.petshop.backend.dto.cart.*;
+import com.petshop.backend.dto.sale.SaleItemRequest;
+import com.petshop.backend.dto.sale.SaleRequest;
import com.petshop.backend.entity.*;
import com.petshop.backend.exception.BusinessException;
import com.petshop.backend.exception.ResourceNotFoundException;
@@ -22,28 +24,36 @@ import java.util.List;
@Service
public class CartService {
+ private static final int LOYALTY_POINTS_PER_DOLLAR = 20;
+
private final CartRepository cartRepository;
private final CartItemRepository cartItemRepository;
private final UserRepository userRepository;
private final StoreRepository storeRepository;
private final ProductRepository productRepository;
private final CouponRepository couponRepository;
+ private final SaleRepository saleRepository;
+ private final SaleService saleService;
@Value("${stripe.secret-key:}")
private String stripeSecretKey;
public CartService(CartRepository cartRepository,
CartItemRepository cartItemRepository,
- UserRepository userRepository,
- StoreRepository storeRepository,
- ProductRepository productRepository,
- CouponRepository couponRepository) {
+ UserRepository userRepository,
+ StoreRepository storeRepository,
+ ProductRepository productRepository,
+ CouponRepository couponRepository,
+ SaleRepository saleRepository,
+ SaleService saleService) {
this.cartRepository = cartRepository;
this.cartItemRepository = cartItemRepository;
this.userRepository = userRepository;
this.storeRepository = storeRepository;
this.productRepository = productRepository;
this.couponRepository = couponRepository;
+ this.saleRepository = saleRepository;
+ this.saleService = saleService;
}
@PostConstruct
@@ -77,10 +87,14 @@ public class CartService {
newCart.setUser(user);
newCart.setStore(store);
newCart.setCartStatus("ACTIVE");
+ newCart.setPointsApplied(false);
+ newCart.setPointsDiscountAmount(BigDecimal.ZERO);
return cartRepository.save(newCart);
});
+ requireNotCheckoutPending(cart);
+
cartItemRepository.findByCartCartIdAndProductProdId(cart.getCartId(), product.getProdId())
.ifPresentOrElse(
existing -> existing.setQuantity(existing.getQuantity() + request.getQuantity()),
@@ -112,6 +126,8 @@ public class CartService {
throw new BusinessException("Cart is not active");
}
+ requireNotCheckoutPending(item.getCart());
+
if (request.getQuantity() < 1) {
throw new BusinessException("Quantity must be at least 1");
}
@@ -136,6 +152,8 @@ public class CartService {
throw new BusinessException("Cart is not active");
}
+ requireNotCheckoutPending(item.getCart());
+
Cart cart = item.getCart();
cartItemRepository.delete(item);
recalculate(cart);
@@ -147,11 +165,14 @@ public class CartService {
public void clearCart(Long userId, Long storeId) {
cartRepository.findActiveCartByUserAndStore(userId, storeId, "ACTIVE")
.ifPresent(cart -> {
+ requireNotCheckoutPending(cart);
cartItemRepository.deleteByCartCartId(cart.getCartId());
cart.setSubtotalAmount(BigDecimal.ZERO);
cart.setDiscountAmount(BigDecimal.ZERO);
+ cart.setPointsDiscountAmount(BigDecimal.ZERO);
cart.setTotalAmount(BigDecimal.ZERO);
cart.setCoupon(null);
+ cart.setPointsApplied(false);
cartRepository.save(cart);
});
}
@@ -162,6 +183,8 @@ public class CartService {
.findActiveCartByUserAndStore(userId, storeId, "ACTIVE")
.orElseThrow(() -> new BusinessException("No active cart found"));
+ requireNotCheckoutPending(cart);
+
Coupon coupon = couponRepository.findByCouponCodeIgnoreCase(couponCode)
.orElseThrow(() -> new BusinessException("Invalid coupon code"));
@@ -189,12 +212,28 @@ public class CartService {
return toResponse(cart);
}
+ @Transactional
+ public CartResponse applyPoints(Long userId, Long storeId, Boolean useLoyaltyPoints) {
+ Cart cart = cartRepository
+ .findActiveCartByUserAndStore(userId, storeId, "ACTIVE")
+ .orElseThrow(() -> new BusinessException("No active cart found"));
+
+ requireNotCheckoutPending(cart);
+ cart.setPointsApplied(Boolean.TRUE.equals(useLoyaltyPoints));
+ recalculate(cart);
+ return toResponse(cart);
+ }
+
@Transactional
public CheckoutResponse checkout(Long userId, Long storeId) {
Cart cart = cartRepository
.findActiveCartByUserAndStore(userId, storeId, "ACTIVE")
.orElseThrow(() -> new BusinessException("No active cart found"));
+ if (Boolean.TRUE.equals(cart.getCheckoutPending())) {
+ throw new BusinessException("Checkout already in progress for this cart");
+ }
+
List items = cartItemRepository.findByCartCartId(cart.getCartId());
if (items.isEmpty()) {
throw new BusinessException("Cart is empty");
@@ -219,6 +258,12 @@ public class CartService {
PaymentIntent intent = PaymentIntent.create(params);
+ cart.setCheckoutPending(true);
+ cart.setCheckoutAmount(cart.getTotalAmount());
+ cart.setCheckoutStartedAt(LocalDateTime.now());
+ cart.setCheckoutPaymentIntentId(intent.getId());
+ cartRepository.save(cart);
+
return new CheckoutResponse(
cart.getCartId(),
intent.getClientSecret(),
@@ -242,7 +287,29 @@ public class CartService {
throw new BusinessException("Payment has not been completed");
}
- Long cartId = Long.parseLong(intent.getMetadata().get("cartId"));
+ String cartIdMetadata = intent.getMetadata() != null ? intent.getMetadata().get("cartId") : null;
+ String userIdMetadata = intent.getMetadata() != null ? intent.getMetadata().get("userId") : null;
+
+ if (cartIdMetadata == null || cartIdMetadata.isBlank()) {
+ throw new BusinessException("Payment metadata is missing cart information");
+ }
+ if (userIdMetadata == null || userIdMetadata.isBlank()) {
+ throw new BusinessException("Payment metadata is missing user information");
+ }
+
+ Long cartId;
+ Long metadataUserId;
+ try {
+ cartId = Long.parseLong(cartIdMetadata);
+ metadataUserId = Long.parseLong(userIdMetadata);
+ } catch (NumberFormatException ex) {
+ throw new BusinessException("Payment metadata is invalid");
+ }
+
+ if (!metadataUserId.equals(userId)) {
+ throw new BusinessException("Unauthorized");
+ }
+
Cart cart = cartRepository.findById(cartId)
.orElseThrow(() -> new BusinessException("Cart not found"));
@@ -250,7 +317,74 @@ public class CartService {
throw new BusinessException("Unauthorized");
}
+ if (!Boolean.TRUE.equals(cart.getCheckoutPending())) {
+ throw new BusinessException("Cart checkout was not initiated");
+ }
+
+ if (!paymentIntentId.equals(cart.getCheckoutPaymentIntentId())) {
+ throw new BusinessException("Payment intent mismatch");
+ }
+
+ if (cart.getCheckoutAmount() == null) {
+ throw new BusinessException("Checkout amount snapshot is missing");
+ }
+
+ if (!cart.getCartStatus().equals("ACTIVE")) {
+ throw new BusinessException("Cart is not in active state");
+ }
+
+ List items = cartItemRepository.findByCartCartId(cart.getCartId());
+ if (items.isEmpty()) {
+ throw new BusinessException("Cart items were removed during checkout");
+ }
+
+ BigDecimal recalculatedTotal = recalculateTotalAmount(cart);
+ if (recalculatedTotal.compareTo(cart.getCheckoutAmount()) != 0) {
+ throw new BusinessException("Cart total changed during checkout");
+ }
+
+ long storedAmountInCents = cart.getCheckoutAmount()
+ .multiply(BigDecimal.valueOf(100))
+ .setScale(0, RoundingMode.HALF_UP)
+ .longValue();
+
+ if (intent.getAmount() != storedAmountInCents) {
+ throw new BusinessException("Stripe charged amount does not match expected amount");
+ }
+
+ if (saleRepository.findByCartCartId(cart.getCartId()).isPresent()) {
+ cart.setCartStatus("CHECKED_OUT");
+ cart.setCheckoutPending(false);
+ cart.setCheckoutAmount(null);
+ cart.setCheckoutStartedAt(null);
+ cart.setCheckoutPaymentIntentId(null);
+ cartRepository.save(cart);
+ return;
+ }
+
+ SaleRequest saleRequest = new SaleRequest();
+ saleRequest.setStoreId(cart.getStore().getStoreId());
+ saleRequest.setCustomerId(cart.getUser().getId());
+ saleRequest.setCartId(cart.getCartId());
+ saleRequest.setCouponId(cart.getCoupon() != null ? cart.getCoupon().getCouponId() : null);
+ saleRequest.setPaymentMethod("Card");
+ saleRequest.setChannel("WEBSITE");
+ saleRequest.setItems(cartItemRepository.findByCartCartId(cart.getCartId()).stream()
+ .map(item -> {
+ SaleItemRequest saleItemRequest = new SaleItemRequest();
+ saleItemRequest.setProdId(item.getProduct().getProdId());
+ saleItemRequest.setQuantity(item.getQuantity());
+ return saleItemRequest;
+ })
+ .toList());
+
+ saleService.createSale(saleRequest);
+
cart.setCartStatus("CHECKED_OUT");
+ cart.setCheckoutPending(false);
+ cart.setCheckoutAmount(null);
+ cart.setCheckoutStartedAt(null);
+ cart.setCheckoutPaymentIntentId(null);
cartRepository.save(cart);
} catch (StripeException e) {
@@ -258,6 +392,12 @@ public class CartService {
}
}
+ private void requireNotCheckoutPending(Cart cart) {
+ if (Boolean.TRUE.equals(cart.getCheckoutPending())) {
+ throw new BusinessException("Cannot modify cart while checkout is in progress");
+ }
+ }
+
private void recalculate(Cart cart) {
List items = cartItemRepository.findByCartCartId(cart.getCartId());
@@ -274,8 +414,8 @@ public class CartService {
if ("PERCENTAGE".equalsIgnoreCase(coupon.getDiscountType())) {
discount = subtotal.multiply(coupon.getDiscountValue())
.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
- }
-
+ }
+
else if ("FIXED".equalsIgnoreCase(coupon.getDiscountType())) {
discount = coupon.getDiscountValue().min(subtotal);
}
@@ -283,10 +423,61 @@ public class CartService {
discount = discount.max(BigDecimal.ZERO).min(subtotal);
cart.setDiscountAmount(discount);
- cart.setTotalAmount(subtotal.subtract(discount).max(BigDecimal.ZERO));
+
+ BigDecimal remainingAfterCoupon = subtotal.subtract(discount).max(BigDecimal.ZERO);
+ BigDecimal pointsDiscount = calculatePointsDiscount(cart.getUser(), remainingAfterCoupon, Boolean.TRUE.equals(cart.getPointsApplied()));
+ cart.setPointsDiscountAmount(pointsDiscount);
+ cart.setTotalAmount(remainingAfterCoupon.subtract(pointsDiscount).max(BigDecimal.ZERO));
cartRepository.save(cart);
}
+ private BigDecimal recalculateTotalAmount(Cart cart) {
+ List items = cartItemRepository.findByCartCartId(cart.getCartId());
+
+ BigDecimal subtotal = items.stream()
+ .map(i -> i.getUnitPrice().multiply(BigDecimal.valueOf(i.getQuantity())))
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+ BigDecimal discount = BigDecimal.ZERO;
+ Coupon coupon = cart.getCoupon();
+
+ if (coupon != null) {
+ if ("PERCENTAGE".equalsIgnoreCase(coupon.getDiscountType())) {
+ discount = subtotal.multiply(coupon.getDiscountValue())
+ .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
+ }
+
+ else if ("FIXED".equalsIgnoreCase(coupon.getDiscountType())) {
+ discount = coupon.getDiscountValue().min(subtotal);
+ }
+ }
+
+ discount = discount.max(BigDecimal.ZERO).min(subtotal);
+
+ BigDecimal remainingAfterCoupon = subtotal.subtract(discount).max(BigDecimal.ZERO);
+ BigDecimal pointsDiscount = calculatePointsDiscount(cart.getUser(), remainingAfterCoupon, Boolean.TRUE.equals(cart.getPointsApplied()));
+
+ return remainingAfterCoupon.subtract(pointsDiscount).max(BigDecimal.ZERO);
+ }
+
+
+ private BigDecimal calculatePointsDiscount(User user, BigDecimal remainingAmount, boolean pointsApplied) {
+ if (!pointsApplied || user == null || remainingAmount.compareTo(BigDecimal.ZERO) <= 0) {
+ return BigDecimal.ZERO;
+ }
+
+ int availablePoints = user.getLoyaltyPoints() != null ? user.getLoyaltyPoints() : 0;
+ int wholeDollars = availablePoints / LOYALTY_POINTS_PER_DOLLAR;
+ if (wholeDollars <= 0) {
+ return BigDecimal.ZERO;
+ }
+
+ BigDecimal maxRedeemable = remainingAmount.setScale(0, RoundingMode.DOWN);
+ return BigDecimal.valueOf(wholeDollars)
+ .min(maxRedeemable)
+ .setScale(2, RoundingMode.HALF_UP);
+ }
+
private CartResponse toResponse(Cart cart) {
List itemResponses = cartItemRepository
.findByCartCartId(cart.getCartId())
@@ -309,8 +500,11 @@ public class CartService {
response.setItems(itemResponses);
response.setSubtotalAmount(cart.getSubtotalAmount());
response.setDiscountAmount(cart.getDiscountAmount());
+ response.setPointsDiscountAmount(cart.getPointsDiscountAmount());
response.setTotalAmount(cart.getTotalAmount());
response.setCouponCode(cart.getCoupon() != null ? cart.getCoupon().getCouponCode() : null);
+ response.setPointsApplied(cart.getPointsApplied());
+ response.setAvailableLoyaltyPoints(cart.getUser() != null ? cart.getUser().getLoyaltyPoints() : null);
return response;
}
diff --git a/backend/src/main/java/com/petshop/backend/service/CatalogImageStorageService.java b/backend/src/main/java/com/petshop/backend/service/CatalogImageStorageService.java
index 34a92ff0..1068e094 100644
--- a/backend/src/main/java/com/petshop/backend/service/CatalogImageStorageService.java
+++ b/backend/src/main/java/com/petshop/backend/service/CatalogImageStorageService.java
@@ -1,5 +1,6 @@
package com.petshop.backend.service;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.PathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
@@ -21,20 +22,31 @@ public class CatalogImageStorageService {
private static final String PET_PREFIX = "/uploads/pets/";
private static final String PRODUCT_PREFIX = "/uploads/products/";
+ @Value("${app.upload.base-dir:uploads}")
+ private String uploadBaseDir;
+
public String storePetImage(MultipartFile file) throws IOException {
- return storeImage(file, Paths.get("uploads", "pets").toAbsolutePath().normalize(), PET_PREFIX);
+ return storeImage(file, Paths.get(uploadBaseDir, "pets").toAbsolutePath().normalize(), PET_PREFIX);
}
public String storeProductImage(MultipartFile file) throws IOException {
- return storeImage(file, Paths.get("uploads", "products").toAbsolutePath().normalize(), PRODUCT_PREFIX);
+ return storeImage(file, Paths.get(uploadBaseDir, "products").toAbsolutePath().normalize(), PRODUCT_PREFIX);
}
public Resource loadPetImage(String storedPath) {
- return new PathResource(resolveStoredPath(storedPath, Paths.get("uploads", "pets").toAbsolutePath().normalize(), PET_PREFIX));
+ Resource resource = new PathResource(resolveStoredPath(storedPath, Paths.get(uploadBaseDir, "pets").toAbsolutePath().normalize(), PET_PREFIX));
+ if (!resource.exists()) {
+ throw new IllegalArgumentException("Image file was not found");
+ }
+ return resource;
}
public Resource loadProductImage(String storedPath) {
- return new PathResource(resolveStoredPath(storedPath, Paths.get("uploads", "products").toAbsolutePath().normalize(), PRODUCT_PREFIX));
+ Resource resource = new PathResource(resolveStoredPath(storedPath, Paths.get(uploadBaseDir, "products").toAbsolutePath().normalize(), PRODUCT_PREFIX));
+ if (!resource.exists()) {
+ throw new IllegalArgumentException("Image file was not found");
+ }
+ return resource;
}
public MediaType resolveMediaType(Resource resource) {
@@ -42,11 +54,11 @@ public class CatalogImageStorageService {
}
public void deletePetImage(String storedPath) throws IOException {
- deleteImage(storedPath, Paths.get("uploads", "pets").toAbsolutePath().normalize(), PET_PREFIX);
+ deleteImage(storedPath, Paths.get(uploadBaseDir, "pets").toAbsolutePath().normalize(), PET_PREFIX);
}
public void deleteProductImage(String storedPath) throws IOException {
- deleteImage(storedPath, Paths.get("uploads", "products").toAbsolutePath().normalize(), PRODUCT_PREFIX);
+ deleteImage(storedPath, Paths.get(uploadBaseDir, "products").toAbsolutePath().normalize(), PRODUCT_PREFIX);
}
private String storeImage(MultipartFile file, Path directory, String prefix) throws IOException {
@@ -90,7 +102,7 @@ public class CatalogImageStorageService {
}
String extension = originalFilename.substring(extensionIndex).toLowerCase(Locale.ROOT);
return switch (extension) {
- case ".jpg", ".jpeg", ".png", ".gif" -> extension;
+ case ".jpg", ".jpeg", ".png", ".gif", ".webp" -> extension;
default -> ".jpg";
};
}
diff --git a/backend/src/main/java/com/petshop/backend/service/PasswordResetService.java b/backend/src/main/java/com/petshop/backend/service/PasswordResetService.java
new file mode 100644
index 00000000..77a14d18
--- /dev/null
+++ b/backend/src/main/java/com/petshop/backend/service/PasswordResetService.java
@@ -0,0 +1,136 @@
+package com.petshop.backend.service;
+
+import com.petshop.backend.dto.auth.ForgotPasswordResponse;
+import com.petshop.backend.dto.auth.ResetPasswordResponse;
+import com.petshop.backend.entity.PasswordResetToken;
+import com.petshop.backend.entity.User;
+import com.petshop.backend.exception.BusinessException;
+import com.petshop.backend.repository.PasswordResetTokenRepository;
+import com.petshop.backend.repository.UserRepository;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.time.LocalDateTime;
+import java.util.Base64;
+import java.util.Optional;
+
+@Service
+public class PasswordResetService {
+
+ private static final int RESET_TOKEN_BYTES = 32;
+ private static final long RESET_TOKEN_MINUTES = 30;
+
+ private final PasswordResetTokenRepository passwordResetTokenRepository;
+ private final UserRepository userRepository;
+ private final PasswordEncoder passwordEncoder;
+ private final SecureRandom secureRandom = new SecureRandom();
+
+ public PasswordResetService(PasswordResetTokenRepository passwordResetTokenRepository,
+ UserRepository userRepository,
+ PasswordEncoder passwordEncoder) {
+ this.passwordResetTokenRepository = passwordResetTokenRepository;
+ this.userRepository = userRepository;
+ this.passwordEncoder = passwordEncoder;
+ }
+
+ @Transactional
+ public ForgotPasswordResponse createResetToken(String usernameOrEmail) {
+ String normalized = trimToNull(usernameOrEmail);
+ if (normalized == null) {
+ throw new BusinessException("Username or email is required");
+ }
+
+ Optional user = userRepository.findByEmail(normalized)
+ .or(() -> userRepository.findByUsername(normalized));
+
+ if (user.isEmpty() || !Boolean.TRUE.equals(user.get().getActive())) {
+ return new ForgotPasswordResponse(
+ "If an account matches that username or email, a reset token has been generated.",
+ null
+ );
+ }
+
+ User managedUser = user.get();
+ LocalDateTime now = LocalDateTime.now();
+ invalidateOutstandingTokens(managedUser.getId(), now);
+
+ String rawToken = generateRawToken();
+ PasswordResetToken resetToken = new PasswordResetToken();
+ resetToken.setUser(managedUser);
+ resetToken.setTokenHash(hashToken(rawToken));
+ resetToken.setExpiresAt(now.plusMinutes(RESET_TOKEN_MINUTES));
+ passwordResetTokenRepository.save(resetToken);
+
+ return new ForgotPasswordResponse(
+ "If an account matches that username or email, a reset token has been generated.",
+ rawToken
+ );
+ }
+
+ @Transactional
+ public ResetPasswordResponse resetPassword(String rawToken, String newPassword) {
+ String normalizedToken = trimToNull(rawToken);
+ if (normalizedToken == null) {
+ throw new BusinessException("Reset token is required");
+ }
+
+ PasswordResetToken token = passwordResetTokenRepository
+ .findByTokenHashAndUsedAtIsNullAndExpiresAtAfter(hashToken(normalizedToken), LocalDateTime.now())
+ .orElseThrow(() -> new BusinessException("Reset token is invalid or has expired"));
+
+ User user = token.getUser();
+ if (!Boolean.TRUE.equals(user.getActive())) {
+ throw new BusinessException("User account is inactive");
+ }
+
+ LocalDateTime now = LocalDateTime.now();
+ user.setPassword(passwordEncoder.encode(newPassword));
+ user.setTokenVersion(user.getTokenVersion() + 1);
+ userRepository.save(user);
+
+ token.setUsedAt(now);
+ passwordResetTokenRepository.save(token);
+ invalidateOutstandingTokens(user.getId(), now);
+
+ return new ResetPasswordResponse("Password reset successfully");
+ }
+
+ private void invalidateOutstandingTokens(Long userId, LocalDateTime usedAt) {
+ for (PasswordResetToken token : passwordResetTokenRepository.findByUser_IdAndUsedAtIsNull(userId)) {
+ token.setUsedAt(usedAt);
+ }
+ }
+
+ private String generateRawToken() {
+ byte[] bytes = new byte[RESET_TOKEN_BYTES];
+ secureRandom.nextBytes(bytes);
+ return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
+ }
+
+ private String hashToken(String rawToken) {
+ try {
+ MessageDigest digest = MessageDigest.getInstance("SHA-256");
+ byte[] hashed = digest.digest(rawToken.getBytes(StandardCharsets.UTF_8));
+ StringBuilder builder = new StringBuilder(hashed.length * 2);
+ for (byte value : hashed) {
+ builder.append(String.format("%02x", value));
+ }
+ return builder.toString();
+ } catch (NoSuchAlgorithmException ex) {
+ throw new IllegalStateException("SHA-256 is not available", ex);
+ }
+ }
+
+ private String trimToNull(String value) {
+ if (value == null) {
+ return null;
+ }
+ String trimmed = value.trim();
+ return trimmed.isEmpty() ? null : trimmed;
+ }
+}
diff --git a/backend/src/main/java/com/petshop/backend/service/SaleService.java b/backend/src/main/java/com/petshop/backend/service/SaleService.java
index 526f5a57..b7c9c498 100644
--- a/backend/src/main/java/com/petshop/backend/service/SaleService.java
+++ b/backend/src/main/java/com/petshop/backend/service/SaleService.java
@@ -22,6 +22,7 @@ import java.util.List;
public class SaleService {
private static final BigDecimal EMPLOYEE_DISCOUNT_PERCENT = new BigDecimal("0.10");
+ private static final int LOYALTY_POINTS_PER_DOLLAR = 20;
private final SaleRepository saleRepository;
private final ProductRepository productRepository;
@@ -56,7 +57,9 @@ public class SaleService {
@Transactional
public SaleResponse createSale(SaleRequest request) {
- User employee = AuthenticationHelper.getAuthenticatedUser(userRepository);
+ User actor = AuthenticationHelper.getAuthenticatedUser(userRepository);
+ boolean websiteSale = request.getChannel() != null && request.getChannel().equalsIgnoreCase("WEBSITE");
+ User employee = websiteSale ? resolveWebsiteSaleEmployee(request.getStoreId()) : actor;
StoreLocation store = storeRepository.findById(request.getStoreId())
.orElseThrow(() -> new ResourceNotFoundException("Store not found with id: " + request.getStoreId()));
@@ -96,6 +99,11 @@ public class SaleService {
sale.setCustomer(customer);
}
+ if (websiteSale && customer == null) {
+ customer = actor;
+ sale.setCustomer(customer);
+ }
+
if (sale.getIsRefund() && request.getOriginalSaleId() != null) {
Sale originalSale = saleRepository.findById(request.getOriginalSaleId())
.orElseThrow(() -> new ResourceNotFoundException("Original sale not found with id: " + request.getOriginalSaleId()));
@@ -152,34 +160,17 @@ public class SaleService {
saleItems.add(saleItem);
subtotalAmount = subtotalAmount.add(itemTotal);
}
-
- Sale originalSale = sale.getOriginalSale();
- BigDecimal originalSubtotal = originalSale.getSubtotalAmount() != null
- ? originalSale.getSubtotalAmount()
- : originalSale.getItems().stream()
- .map(i -> i.getUnitPrice().multiply(BigDecimal.valueOf(Math.abs(i.getQuantity()))))
- .reduce(BigDecimal.ZERO, BigDecimal::add);
-
- BigDecimal refundRatio = originalSubtotal.compareTo(BigDecimal.ZERO) != 0
- ? subtotalAmount.divide(originalSubtotal, 10, RoundingMode.HALF_UP)
- : BigDecimal.ONE;
-
- BigDecimal refundTotal = originalSale.getTotalAmount().abs()
- .multiply(refundRatio).setScale(2, RoundingMode.HALF_UP);
-
- sale.setSubtotalAmount(subtotalAmount.negate());
- sale.setTotalAmount(refundTotal.negate());
-
- User refundCustomer = customer != null ? customer : originalSale.getCustomer();
- if (refundCustomer != null) {
- int pointsToRestore = BigDecimal.valueOf(originalSale.getPointsUsed())
- .multiply(refundRatio).setScale(0, RoundingMode.FLOOR).intValue();
- int pointsToDeduct = BigDecimal.valueOf(originalSale.getPointsEarned())
- .multiply(refundRatio).setScale(0, RoundingMode.FLOOR).intValue();
- refundCustomer.setLoyaltyPoints(refundCustomer.getLoyaltyPoints() + pointsToRestore - pointsToDeduct);
- userRepository.save(refundCustomer);
- }
+ subtotalAmount = subtotalAmount.negate();
+ sale.setSubtotalAmount(subtotalAmount);
+ sale.setTotalAmount(subtotalAmount);
+ sale.setCouponDiscountAmount(BigDecimal.ZERO);
+ sale.setEmployeeDiscountAmount(BigDecimal.ZERO);
+ sale.setLoyaltyDiscountAmount(BigDecimal.ZERO);
+ sale.setPointsEarned(0);
} else {
+ if (request.getItems() == null || request.getItems().isEmpty()) {
+ throw new BusinessException("At least one item is required");
+ }
for (var itemRequest : request.getItems()) {
Product product = productRepository.findById(itemRequest.getProdId())
.orElseThrow(() -> new ResourceNotFoundException("Product not found with id: " + itemRequest.getProdId()));
@@ -212,28 +203,22 @@ public class SaleService {
BigDecimal couponDiscount = calculateCouponDiscount(sale.getCoupon(), subtotalAmount);
sale.setCouponDiscountAmount(couponDiscount);
- BigDecimal pointsDiscount = BigDecimal.ZERO;
- int pointsUsed = 0;
- if (customer != null && request.getPointsUsed() != null && request.getPointsUsed() > 0) {
- if (customer.getLoyaltyPoints() < request.getPointsUsed()) {
- throw new BusinessException("Customer does not have enough loyalty points");
- }
- pointsUsed = request.getPointsUsed();
- pointsDiscount = calculatePointsDiscount(pointsUsed);
- customer.setLoyaltyPoints(customer.getLoyaltyPoints() - pointsUsed);
- }
- sale.setPointsUsed(pointsUsed);
- sale.setPointsDiscountAmount(pointsDiscount);
-
- BigDecimal employeeDiscount = calculateEmployeeDiscount(customer, subtotalAmount.subtract(couponDiscount).subtract(pointsDiscount));
+ BigDecimal employeeDiscount = calculateEmployeeDiscount(customer, subtotalAmount.subtract(couponDiscount));
sale.setEmployeeDiscountAmount(employeeDiscount);
- BigDecimal finalTotal = subtotalAmount.subtract(couponDiscount).subtract(pointsDiscount).subtract(employeeDiscount);
+ boolean useLoyaltyPoints = sale.getCart() != null && Boolean.TRUE.equals(sale.getCart().getPointsApplied());
+ BigDecimal loyaltyDiscount = calculateLoyaltyDiscount(customer, subtotalAmount.subtract(couponDiscount).subtract(employeeDiscount), useLoyaltyPoints);
+ sale.setLoyaltyDiscountAmount(loyaltyDiscount);
+
+ BigDecimal finalTotal = subtotalAmount.subtract(couponDiscount).subtract(employeeDiscount).subtract(loyaltyDiscount);
sale.setTotalAmount(finalTotal.max(BigDecimal.ZERO));
+ int pointsUsed = toPointsUsed(loyaltyDiscount);
sale.setPointsEarned(sale.getTotalAmount().setScale(0, RoundingMode.FLOOR).intValue());
if (customer != null) {
- customer.setLoyaltyPoints(customer.getLoyaltyPoints() + sale.getPointsEarned());
+ int currentPoints = customer.getLoyaltyPoints() != null ? customer.getLoyaltyPoints() : 0;
+ int updatedPoints = currentPoints - pointsUsed + sale.getPointsEarned();
+ customer.setLoyaltyPoints(Math.max(updatedPoints, 0));
userRepository.save(customer);
}
}
@@ -277,10 +262,6 @@ public class SaleService {
return discount.min(subtotal).setScale(2, RoundingMode.HALF_UP);
}
- private BigDecimal calculatePointsDiscount(int pointsUsed) {
- return new BigDecimal(pointsUsed).divide(new BigDecimal("20"), 2, RoundingMode.HALF_UP);
- }
-
private BigDecimal calculateEmployeeDiscount(User customer, BigDecimal remainingAmount) {
if (customer == null || remainingAmount.compareTo(BigDecimal.ZERO) <= 0) {
return BigDecimal.ZERO;
@@ -293,6 +274,36 @@ public class SaleService {
return BigDecimal.ZERO;
}
+ private BigDecimal calculateLoyaltyDiscount(User customer, BigDecimal remainingAmount, boolean useLoyaltyPoints) {
+ if (!useLoyaltyPoints || customer == null || remainingAmount.compareTo(BigDecimal.ZERO) <= 0) {
+ return BigDecimal.ZERO;
+ }
+
+ int availablePoints = customer.getLoyaltyPoints() != null ? customer.getLoyaltyPoints() : 0;
+ int wholeDollars = availablePoints / LOYALTY_POINTS_PER_DOLLAR;
+ if (wholeDollars <= 0) {
+ return BigDecimal.ZERO;
+ }
+
+ BigDecimal maxRedeemable = remainingAmount.setScale(0, RoundingMode.DOWN);
+ return BigDecimal.valueOf(wholeDollars)
+ .min(maxRedeemable)
+ .setScale(2, RoundingMode.HALF_UP);
+ }
+
+ private int toPointsUsed(BigDecimal loyaltyDiscount) {
+ if (loyaltyDiscount == null || loyaltyDiscount.compareTo(BigDecimal.ZERO) <= 0) {
+ return 0;
+ }
+ return loyaltyDiscount.setScale(0, RoundingMode.DOWN).intValue() * LOYALTY_POINTS_PER_DOLLAR;
+ }
+
+ private User resolveWebsiteSaleEmployee(Long storeId) {
+ return userRepository.findFirstByPrimaryStoreStoreIdAndRoleAndActiveTrueOrderByIdAsc(storeId, User.Role.STAFF)
+ .or(() -> userRepository.findFirstByRoleAndActiveTrueOrderByIdAsc(User.Role.ADMIN))
+ .orElseThrow(() -> new BusinessException("No active employee available for website sale"));
+ }
+
private SaleResponse mapToResponse(Sale sale) {
SaleResponse response = new SaleResponse();
response.setSaleId(sale.getSaleId());
@@ -314,9 +325,8 @@ public class SaleService {
response.setSubtotalAmount(sale.getSubtotalAmount());
response.setCouponDiscountAmount(sale.getCouponDiscountAmount());
response.setEmployeeDiscountAmount(sale.getEmployeeDiscountAmount());
+ response.setLoyaltyDiscountAmount(sale.getLoyaltyDiscountAmount());
response.setPointsEarned(sale.getPointsEarned());
- response.setPointsUsed(sale.getPointsUsed());
- response.setPointsDiscountAmount(sale.getPointsDiscountAmount());
response.setChannel(sale.getChannel());
if (sale.getCoupon() != null) {
response.setCouponId(sale.getCoupon().getCouponId());
diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml
index d16835fb..e3c3e0df 100644
--- a/backend/src/main/resources/application.yml
+++ b/backend/src/main/resources/application.yml
@@ -47,6 +47,10 @@ springdoc:
swagger-ui:
path: /swagger-ui
+app:
+ upload:
+ base-dir: ${UPLOAD_BASE_DIR:uploads}
+
jwt:
secret: ${JWT_SECRET}
expiration: ${JWT_EXPIRATION:86400000}
diff --git a/backend/src/main/resources/db/migration/V1__target_baseline.sql b/backend/src/main/resources/db/migration/V1__target_baseline.sql
index d287c8e1..bc064913 100644
--- a/backend/src/main/resources/db/migration/V1__target_baseline.sql
+++ b/backend/src/main/resources/db/migration/V1__target_baseline.sql
@@ -1,4 +1,3 @@
-
CREATE TABLE IF NOT EXISTS storeLocation (
storeId BIGINT AUTO_INCREMENT PRIMARY KEY,
storeName VARCHAR(100) NOT NULL,
@@ -192,6 +191,12 @@ CREATE TABLE IF NOT EXISTS cart (
subtotalAmount DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
discountAmount DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
totalAmount DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
+ pointsApplied BOOLEAN NOT NULL DEFAULT FALSE,
+ pointsDiscountAmount DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
+ checkoutPending BOOLEAN NOT NULL DEFAULT FALSE,
+ checkoutAmount DECIMAL(10, 2),
+ checkoutStartedAt DATETIME,
+ checkoutPaymentIntentId VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_cart_user FOREIGN KEY (userId) REFERENCES users(id),
@@ -227,9 +232,13 @@ CREATE TABLE IF NOT EXISTS sale (
subtotalAmount DECIMAL(10, 2) NULL,
couponDiscountAmount DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
employeeDiscountAmount DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
+ pointsUsed INT NOT NULL DEFAULT 0,
+ loyaltyDiscountAmount DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
pointsEarned INT NOT NULL DEFAULT 0,
+ pointsDiscountAmount DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ CONSTRAINT uq_sale_cart_id UNIQUE (cartId),
CONSTRAINT fk_sale_employee FOREIGN KEY (employeeId) REFERENCES users(id),
CONSTRAINT fk_sale_store FOREIGN KEY (storeId) REFERENCES storeLocation(storeId),
CONSTRAINT fk_sale_customer FOREIGN KEY (customerId) REFERENCES users(id) ON DELETE SET NULL,
@@ -275,6 +284,20 @@ CREATE TABLE IF NOT EXISTS refund_item (
CONSTRAINT fk_refund_item_product FOREIGN KEY (prod_id) REFERENCES product(prodId)
);
+CREATE TABLE IF NOT EXISTS passwordResetToken (
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
+ userId BIGINT NOT NULL,
+ tokenHash VARCHAR(64) NOT NULL,
+ expiresAt DATETIME NOT NULL,
+ usedAt DATETIME NULL,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ CONSTRAINT uq_password_reset_token_hash UNIQUE (tokenHash),
+ CONSTRAINT fk_password_reset_token_user FOREIGN KEY (userId) REFERENCES users(id) ON DELETE CASCADE
+);
+
+CREATE INDEX idx_password_reset_token_user ON passwordResetToken(userId);
+CREATE INDEX idx_password_reset_token_expires ON passwordResetToken(expiresAt);
+
CREATE TABLE IF NOT EXISTS conversation (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
customerId BIGINT NOT NULL,
@@ -307,6 +330,10 @@ CREATE TABLE IF NOT EXISTS activityLog (
logId BIGINT AUTO_INCREMENT PRIMARY KEY,
userId BIGINT NOT NULL,
storeId BIGINT NULL,
+ usernameSnapshot VARCHAR(50) NULL,
+ fullNameSnapshot VARCHAR(100) NULL,
+ roleSnapshot VARCHAR(20) NULL,
+ storeNameSnapshot VARCHAR(100) NULL,
activity TEXT NOT NULL,
logTimestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_activity_log_user FOREIGN KEY (userId) REFERENCES users(id),
@@ -339,3 +366,4 @@ CREATE INDEX idx_cart_user ON cart(userId);
CREATE INDEX idx_conversation_customer ON conversation(customerId);
CREATE INDEX idx_conversation_staff ON conversation(staffId);
CREATE INDEX idx_activity_log_store ON activityLog(storeId);
+CREATE INDEX idx_activity_log_timestamp_id ON activityLog(logTimestamp, logId);
diff --git a/backend/src/main/resources/db/migration/V2__seed_data.sql b/backend/src/main/resources/db/migration/V2__seed_data.sql
index 970c8b43..88d3835b 100644
--- a/backend/src/main/resources/db/migration/V2__seed_data.sql
+++ b/backend/src/main/resources/db/migration/V2__seed_data.sql
@@ -4,6 +4,7 @@ SET FOREIGN_KEY_CHECKS = 0;
DELETE FROM activityLog;
DELETE FROM message;
DELETE FROM conversation;
+DELETE FROM passwordResetToken;
DELETE FROM refund_item;
DELETE FROM refund;
DELETE FROM saleItem;
@@ -45,6 +46,7 @@ ALTER TABLE refund AUTO_INCREMENT = 1;
ALTER TABLE refund_item AUTO_INCREMENT = 1;
ALTER TABLE conversation AUTO_INCREMENT = 1;
ALTER TABLE message AUTO_INCREMENT = 1;
+ALTER TABLE passwordResetToken AUTO_INCREMENT = 1;
ALTER TABLE activityLog AUTO_INCREMENT = 1;
SET FOREIGN_KEY_CHECKS = 1;
@@ -1739,3 +1741,509 @@ INSERT INTO message (id, conversationId, senderId, content, attachmentUrl, attac
(118, 30, 8, 'Happy to help. Please share the order number or the pet name involved.', NULL, NULL, NULL, NULL, '2026-03-02 09:05:00', 1),
(119, 30, 45, 'Order #1030 is the one I meant, and the pet is Kiki.', NULL, NULL, NULL, NULL, '2026-03-02 09:10:00', 1),
(120, 30, 8, 'Thanks, the account and order are now updated on this conversation.', NULL, NULL, NULL, NULL, '2026-03-02 09:15:00', 0);
+
+
+INSERT INTO users (username, password, email, firstName, lastName, fullName, phone, avatarUrl, role, staffRole, primaryStoreId, loyaltyPoints, active, tokenVersion)
+SELECT 'ai.bot',
+ '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq',
+ 'bot@petshop.com',
+ 'AI',
+ 'Bot',
+ 'AI Bot',
+ '000-000-0000',
+ 'https://images.petshop.local/users/bot.webp',
+ 'STAFF',
+ 'CUSTOMER_SERVICE',
+ NULL,
+ 0,
+ 1,
+ 0
+FROM DUAL
+WHERE NOT EXISTS (
+ SELECT 1
+ FROM users
+ WHERE username = 'ai.bot'
+);
+
+UPDATE users
+SET
+ username = CASE id
+ WHEN 15 THEN 'customer'
+ WHEN 16 THEN 'maya.brown'
+ WHEN 17 THEN 'noah.clark'
+ WHEN 18 THEN 'avery.wilson'
+ WHEN 19 THEN 'leah.martinez'
+ WHEN 20 THEN 'julian.anderson'
+ WHEN 21 THEN 'zoe.taylor'
+ WHEN 22 THEN 'ethan.parker'
+ WHEN 23 THEN 'ruby.evans'
+ WHEN 24 THEN 'caleb.scott'
+ WHEN 25 THEN 'ivy.adams'
+ WHEN 26 THEN 'isaac.baker'
+ WHEN 27 THEN 'hannah.hall'
+ WHEN 28 THEN 'mason.rivera'
+ WHEN 29 THEN 'aria.mitchell'
+ WHEN 30 THEN 'wyatt.collins'
+ WHEN 31 THEN 'elena.morris'
+ WHEN 32 THEN 'leo.cook'
+ WHEN 33 THEN 'grace.bell'
+ WHEN 34 THEN 'hudson.reed'
+ WHEN 35 THEN 'claire.murphy'
+ WHEN 36 THEN 'omar.bailey'
+ WHEN 37 THEN 'naomi.cooper'
+ WHEN 38 THEN 'jasper.richardson'
+ WHEN 39 THEN 'sofia.cox'
+ WHEN 40 THEN 'miles.howard'
+ WHEN 41 THEN 'audrey.ward'
+ WHEN 42 THEN 'nathan.torres'
+ WHEN 43 THEN 'jade.peterson'
+ WHEN 44 THEN 'rowan.gray'
+ WHEN 45 THEN 'lila.ramirez'
+ WHEN 46 THEN 'eli.james'
+ WHEN 47 THEN 'violet.watson'
+ WHEN 48 THEN 'gavin.brooks'
+ WHEN 49 THEN 'stella.kelly'
+ WHEN 50 THEN 'adrian.sanders'
+ WHEN 51 THEN 'hazel.price'
+ WHEN 52 THEN 'connor.bennett'
+ WHEN 53 THEN 'sadie.wood'
+ WHEN 54 THEN 'xavier.barnes'
+ WHEN 55 THEN 'alice.ross'
+ WHEN 56 THEN 'roman.henderson'
+ WHEN 57 THEN 'lucy.coleman'
+ WHEN 58 THEN 'evan.jenkins'
+ WHEN 59 THEN 'mila.perry'
+ WHEN 60 THEN 'cole.powell'
+ WHEN 61 THEN 'nora.long'
+ WHEN 62 THEN 'adam.patterson'
+ WHEN 63 THEN 'layla.hughes'
+ WHEN 64 THEN 'blake.flores'
+ WHEN 65 THEN 'ellie.washington'
+ WHEN 66 THEN 'ryan.butler'
+ WHEN 67 THEN 'cora.simmons'
+ WHEN 68 THEN 'simon.foster'
+ WHEN 69 THEN 'piper.gonzales'
+ WHEN 70 THEN 'joel.bryant'
+ WHEN 71 THEN 'eva.alexander'
+ WHEN 72 THEN 'felix.russell'
+ WHEN 73 THEN 'maeve.griffin'
+ WHEN 74 THEN 'tristan.diaz'
+ WHEN 75 THEN 'ariana.hayes'
+ WHEN 76 THEN 'declan.myers'
+ WHEN 77 THEN 'brooke.ford'
+ WHEN 78 THEN 'micah.hamilton'
+ WHEN 79 THEN 'bianca.graham'
+ WHEN 80 THEN 'jonah.sullivan'
+ WHEN 81 THEN 'tessa.wallace'
+ WHEN 82 THEN 'damian.woods'
+ WHEN 83 THEN 'riley.cole'
+ WHEN 84 THEN 'kieran.west'
+ WHEN 85 THEN 'sienna.jordan'
+ WHEN 86 THEN 'finley.owens'
+ WHEN 87 THEN 'maren.reynolds'
+ WHEN 88 THEN 'asher.fisher'
+ WHEN 89 THEN 'daphne.ellis'
+ WHEN 90 THEN 'bennett.harrison'
+ WHEN 91 THEN 'selena.gibson'
+ WHEN 92 THEN 'emmett.mcdonald'
+ WHEN 93 THEN 'phoebe.cruz'
+ WHEN 94 THEN 'sawyer.marshall'
+ WHEN 95 THEN 'keira.ortiz'
+ WHEN 96 THEN 'landon.gomez'
+ WHEN 97 THEN 'rosalie.murray'
+ WHEN 98 THEN 'malik.freeman'
+ WHEN 99 THEN 'esme.wells'
+ WHEN 100 THEN 'holden.webb'
+ ELSE username
+ END,
+ email = CASE id
+ WHEN 15 THEN 'customer@petshop.com'
+ WHEN 16 THEN 'maya.brown@gmail.com'
+ WHEN 17 THEN 'noah.clark@gmail.com'
+ WHEN 18 THEN 'avery.wilson@gmail.com'
+ WHEN 19 THEN 'leah.martinez@gmail.com'
+ WHEN 20 THEN 'julian.anderson@gmail.com'
+ WHEN 21 THEN 'zoe.taylor@gmail.com'
+ WHEN 22 THEN 'ethan.parker@gmail.com'
+ WHEN 23 THEN 'ruby.evans@gmail.com'
+ WHEN 24 THEN 'caleb.scott@gmail.com'
+ WHEN 25 THEN 'ivy.adams@gmail.com'
+ WHEN 26 THEN 'isaac.baker@gmail.com'
+ WHEN 27 THEN 'hannah.hall@gmail.com'
+ WHEN 28 THEN 'mason.rivera@gmail.com'
+ WHEN 29 THEN 'aria.mitchell@gmail.com'
+ WHEN 30 THEN 'wyatt.collins@gmail.com'
+ WHEN 31 THEN 'elena.morris@gmail.com'
+ WHEN 32 THEN 'leo.cook@gmail.com'
+ WHEN 33 THEN 'grace.bell@gmail.com'
+ WHEN 34 THEN 'hudson.reed@gmail.com'
+ WHEN 35 THEN 'claire.murphy@gmail.com'
+ WHEN 36 THEN 'omar.bailey@gmail.com'
+ WHEN 37 THEN 'naomi.cooper@gmail.com'
+ WHEN 38 THEN 'jasper.richardson@gmail.com'
+ WHEN 39 THEN 'sofia.cox@gmail.com'
+ WHEN 40 THEN 'miles.howard@gmail.com'
+ WHEN 41 THEN 'audrey.ward@gmail.com'
+ WHEN 42 THEN 'nathan.torres@gmail.com'
+ WHEN 43 THEN 'jade.peterson@gmail.com'
+ WHEN 44 THEN 'rowan.gray@gmail.com'
+ WHEN 45 THEN 'lila.ramirez@gmail.com'
+ WHEN 46 THEN 'eli.james@gmail.com'
+ WHEN 47 THEN 'violet.watson@gmail.com'
+ WHEN 48 THEN 'gavin.brooks@gmail.com'
+ WHEN 49 THEN 'stella.kelly@gmail.com'
+ WHEN 50 THEN 'adrian.sanders@gmail.com'
+ WHEN 51 THEN 'hazel.price@gmail.com'
+ WHEN 52 THEN 'connor.bennett@gmail.com'
+ WHEN 53 THEN 'sadie.wood@gmail.com'
+ WHEN 54 THEN 'xavier.barnes@gmail.com'
+ WHEN 55 THEN 'alice.ross@gmail.com'
+ WHEN 56 THEN 'roman.henderson@gmail.com'
+ WHEN 57 THEN 'lucy.coleman@gmail.com'
+ WHEN 58 THEN 'evan.jenkins@gmail.com'
+ WHEN 59 THEN 'mila.perry@gmail.com'
+ WHEN 60 THEN 'cole.powell@gmail.com'
+ WHEN 61 THEN 'nora.long@gmail.com'
+ WHEN 62 THEN 'adam.patterson@gmail.com'
+ WHEN 63 THEN 'layla.hughes@gmail.com'
+ WHEN 64 THEN 'blake.flores@gmail.com'
+ WHEN 65 THEN 'ellie.washington@gmail.com'
+ WHEN 66 THEN 'ryan.butler@gmail.com'
+ WHEN 67 THEN 'cora.simmons@gmail.com'
+ WHEN 68 THEN 'simon.foster@gmail.com'
+ WHEN 69 THEN 'piper.gonzales@gmail.com'
+ WHEN 70 THEN 'joel.bryant@gmail.com'
+ WHEN 71 THEN 'eva.alexander@gmail.com'
+ WHEN 72 THEN 'felix.russell@gmail.com'
+ WHEN 73 THEN 'maeve.griffin@gmail.com'
+ WHEN 74 THEN 'tristan.diaz@gmail.com'
+ WHEN 75 THEN 'ariana.hayes@gmail.com'
+ WHEN 76 THEN 'declan.myers@gmail.com'
+ WHEN 77 THEN 'brooke.ford@gmail.com'
+ WHEN 78 THEN 'micah.hamilton@gmail.com'
+ WHEN 79 THEN 'bianca.graham@gmail.com'
+ WHEN 80 THEN 'jonah.sullivan@gmail.com'
+ WHEN 81 THEN 'tessa.wallace@gmail.com'
+ WHEN 82 THEN 'damian.woods@gmail.com'
+ WHEN 83 THEN 'riley.cole@gmail.com'
+ WHEN 84 THEN 'kieran.west@gmail.com'
+ WHEN 85 THEN 'sienna.jordan@gmail.com'
+ WHEN 86 THEN 'finley.owens@gmail.com'
+ WHEN 87 THEN 'maren.reynolds@gmail.com'
+ WHEN 88 THEN 'asher.fisher@gmail.com'
+ WHEN 89 THEN 'daphne.ellis@gmail.com'
+ WHEN 90 THEN 'bennett.harrison@gmail.com'
+ WHEN 91 THEN 'selena.gibson@gmail.com'
+ WHEN 92 THEN 'emmett.mcdonald@gmail.com'
+ WHEN 93 THEN 'phoebe.cruz@gmail.com'
+ WHEN 94 THEN 'sawyer.marshall@gmail.com'
+ WHEN 95 THEN 'keira.ortiz@gmail.com'
+ WHEN 96 THEN 'landon.gomez@gmail.com'
+ WHEN 97 THEN 'rosalie.murray@gmail.com'
+ WHEN 98 THEN 'malik.freeman@gmail.com'
+ WHEN 99 THEN 'esme.wells@gmail.com'
+ WHEN 100 THEN 'holden.webb@gmail.com'
+ ELSE email
+ END,
+ firstName = CASE id
+ WHEN 15 THEN 'Test'
+ WHEN 16 THEN 'Maya'
+ WHEN 17 THEN 'Noah'
+ WHEN 18 THEN 'Avery'
+ WHEN 19 THEN 'Leah'
+ WHEN 20 THEN 'Julian'
+ WHEN 21 THEN 'Zoe'
+ WHEN 22 THEN 'Ethan'
+ WHEN 23 THEN 'Ruby'
+ WHEN 24 THEN 'Caleb'
+ WHEN 25 THEN 'Ivy'
+ WHEN 26 THEN 'Isaac'
+ WHEN 27 THEN 'Hannah'
+ WHEN 28 THEN 'Mason'
+ WHEN 29 THEN 'Aria'
+ WHEN 30 THEN 'Wyatt'
+ WHEN 31 THEN 'Elena'
+ WHEN 32 THEN 'Leo'
+ WHEN 33 THEN 'Grace'
+ WHEN 34 THEN 'Hudson'
+ WHEN 35 THEN 'Claire'
+ WHEN 36 THEN 'Omar'
+ WHEN 37 THEN 'Naomi'
+ WHEN 38 THEN 'Jasper'
+ WHEN 39 THEN 'Sofia'
+ WHEN 40 THEN 'Miles'
+ WHEN 41 THEN 'Audrey'
+ WHEN 42 THEN 'Nathan'
+ WHEN 43 THEN 'Jade'
+ WHEN 44 THEN 'Rowan'
+ WHEN 45 THEN 'Lila'
+ WHEN 46 THEN 'Eli'
+ WHEN 47 THEN 'Violet'
+ WHEN 48 THEN 'Gavin'
+ WHEN 49 THEN 'Stella'
+ WHEN 50 THEN 'Adrian'
+ WHEN 51 THEN 'Hazel'
+ WHEN 52 THEN 'Connor'
+ WHEN 53 THEN 'Sadie'
+ WHEN 54 THEN 'Xavier'
+ WHEN 55 THEN 'Alice'
+ WHEN 56 THEN 'Roman'
+ WHEN 57 THEN 'Lucy'
+ WHEN 58 THEN 'Evan'
+ WHEN 59 THEN 'Mila'
+ WHEN 60 THEN 'Cole'
+ WHEN 61 THEN 'Nora'
+ WHEN 62 THEN 'Adam'
+ WHEN 63 THEN 'Layla'
+ WHEN 64 THEN 'Blake'
+ WHEN 65 THEN 'Ellie'
+ WHEN 66 THEN 'Ryan'
+ WHEN 67 THEN 'Cora'
+ WHEN 68 THEN 'Simon'
+ WHEN 69 THEN 'Piper'
+ WHEN 70 THEN 'Joel'
+ WHEN 71 THEN 'Eva'
+ WHEN 72 THEN 'Felix'
+ WHEN 73 THEN 'Maeve'
+ WHEN 74 THEN 'Tristan'
+ WHEN 75 THEN 'Ariana'
+ WHEN 76 THEN 'Declan'
+ WHEN 77 THEN 'Brooke'
+ WHEN 78 THEN 'Micah'
+ WHEN 79 THEN 'Bianca'
+ WHEN 80 THEN 'Jonah'
+ WHEN 81 THEN 'Tessa'
+ WHEN 82 THEN 'Damian'
+ WHEN 83 THEN 'Riley'
+ WHEN 84 THEN 'Kieran'
+ WHEN 85 THEN 'Sienna'
+ WHEN 86 THEN 'Finley'
+ WHEN 87 THEN 'Maren'
+ WHEN 88 THEN 'Asher'
+ WHEN 89 THEN 'Daphne'
+ WHEN 90 THEN 'Bennett'
+ WHEN 91 THEN 'Selena'
+ WHEN 92 THEN 'Emmett'
+ WHEN 93 THEN 'Phoebe'
+ WHEN 94 THEN 'Sawyer'
+ WHEN 95 THEN 'Keira'
+ WHEN 96 THEN 'Landon'
+ WHEN 97 THEN 'Rosalie'
+ WHEN 98 THEN 'Malik'
+ WHEN 99 THEN 'Esme'
+ WHEN 100 THEN 'Holden'
+ ELSE firstName
+ END,
+ fullName = CASE id
+ WHEN 15 THEN 'Test Customer'
+ WHEN 16 THEN 'Maya Brown'
+ WHEN 17 THEN 'Noah Clark'
+ WHEN 18 THEN 'Avery Wilson'
+ WHEN 19 THEN 'Leah Martinez'
+ WHEN 20 THEN 'Julian Anderson'
+ WHEN 21 THEN 'Zoe Taylor'
+ WHEN 22 THEN 'Ethan Parker'
+ WHEN 23 THEN 'Ruby Evans'
+ WHEN 24 THEN 'Caleb Scott'
+ WHEN 25 THEN 'Ivy Adams'
+ WHEN 26 THEN 'Isaac Baker'
+ WHEN 27 THEN 'Hannah Hall'
+ WHEN 28 THEN 'Mason Rivera'
+ WHEN 29 THEN 'Aria Mitchell'
+ WHEN 30 THEN 'Wyatt Collins'
+ WHEN 31 THEN 'Elena Morris'
+ WHEN 32 THEN 'Leo Cook'
+ WHEN 33 THEN 'Grace Bell'
+ WHEN 34 THEN 'Hudson Reed'
+ WHEN 35 THEN 'Claire Murphy'
+ WHEN 36 THEN 'Omar Bailey'
+ WHEN 37 THEN 'Naomi Cooper'
+ WHEN 38 THEN 'Jasper Richardson'
+ WHEN 39 THEN 'Sofia Cox'
+ WHEN 40 THEN 'Miles Howard'
+ WHEN 41 THEN 'Audrey Ward'
+ WHEN 42 THEN 'Nathan Torres'
+ WHEN 43 THEN 'Jade Peterson'
+ WHEN 44 THEN 'Rowan Gray'
+ WHEN 45 THEN 'Lila Ramirez'
+ WHEN 46 THEN 'Eli James'
+ WHEN 47 THEN 'Violet Watson'
+ WHEN 48 THEN 'Gavin Brooks'
+ WHEN 49 THEN 'Stella Kelly'
+ WHEN 50 THEN 'Adrian Sanders'
+ WHEN 51 THEN 'Hazel Price'
+ WHEN 52 THEN 'Connor Bennett'
+ WHEN 53 THEN 'Sadie Wood'
+ WHEN 54 THEN 'Xavier Barnes'
+ WHEN 55 THEN 'Alice Ross'
+ WHEN 56 THEN 'Roman Henderson'
+ WHEN 57 THEN 'Lucy Coleman'
+ WHEN 58 THEN 'Evan Jenkins'
+ WHEN 59 THEN 'Mila Perry'
+ WHEN 60 THEN 'Cole Powell'
+ WHEN 61 THEN 'Nora Long'
+ WHEN 62 THEN 'Adam Patterson'
+ WHEN 63 THEN 'Layla Hughes'
+ WHEN 64 THEN 'Blake Flores'
+ WHEN 65 THEN 'Ellie Washington'
+ WHEN 66 THEN 'Ryan Butler'
+ WHEN 67 THEN 'Cora Simmons'
+ WHEN 68 THEN 'Simon Foster'
+ WHEN 69 THEN 'Piper Gonzales'
+ WHEN 70 THEN 'Joel Bryant'
+ WHEN 71 THEN 'Eva Alexander'
+ WHEN 72 THEN 'Felix Russell'
+ WHEN 73 THEN 'Maeve Griffin'
+ WHEN 74 THEN 'Tristan Diaz'
+ WHEN 75 THEN 'Ariana Hayes'
+ WHEN 76 THEN 'Declan Myers'
+ WHEN 77 THEN 'Brooke Ford'
+ WHEN 78 THEN 'Micah Hamilton'
+ WHEN 79 THEN 'Bianca Graham'
+ WHEN 80 THEN 'Jonah Sullivan'
+ WHEN 81 THEN 'Tessa Wallace'
+ WHEN 82 THEN 'Damian Woods'
+ WHEN 83 THEN 'Riley Cole'
+ WHEN 84 THEN 'Kieran West'
+ WHEN 85 THEN 'Sienna Jordan'
+ WHEN 86 THEN 'Finley Owens'
+ WHEN 87 THEN 'Maren Reynolds'
+ WHEN 88 THEN 'Asher Fisher'
+ WHEN 89 THEN 'Daphne Ellis'
+ WHEN 90 THEN 'Bennett Harrison'
+ WHEN 91 THEN 'Selena Gibson'
+ WHEN 92 THEN 'Emmett Mcdonald'
+ WHEN 93 THEN 'Phoebe Cruz'
+ WHEN 94 THEN 'Sawyer Marshall'
+ WHEN 95 THEN 'Keira Ortiz'
+ WHEN 96 THEN 'Landon Gomez'
+ WHEN 97 THEN 'Rosalie Murray'
+ WHEN 98 THEN 'Malik Freeman'
+ WHEN 99 THEN 'Esme Wells'
+ WHEN 100 THEN 'Holden Webb'
+ ELSE fullName
+ END,
+ primaryStoreId = CASE id
+ WHEN 15 THEN 1
+ WHEN 16 THEN 2
+ WHEN 17 THEN 3
+ WHEN 18 THEN 1
+ WHEN 19 THEN 2
+ WHEN 20 THEN 3
+ WHEN 21 THEN 1
+ WHEN 22 THEN 2
+ WHEN 23 THEN 3
+ WHEN 24 THEN 1
+ WHEN 25 THEN 2
+ WHEN 26 THEN 3
+ WHEN 27 THEN 1
+ WHEN 28 THEN 2
+ WHEN 29 THEN 3
+ WHEN 30 THEN 1
+ WHEN 31 THEN 2
+ WHEN 32 THEN 3
+ WHEN 33 THEN 1
+ WHEN 34 THEN 2
+ WHEN 35 THEN 3
+ WHEN 36 THEN 1
+ WHEN 37 THEN 2
+ WHEN 38 THEN 3
+ WHEN 39 THEN 1
+ WHEN 40 THEN 2
+ WHEN 41 THEN 3
+ WHEN 42 THEN 1
+ WHEN 43 THEN 2
+ WHEN 44 THEN 3
+ WHEN 45 THEN 1
+ WHEN 46 THEN 2
+ WHEN 47 THEN 3
+ WHEN 48 THEN 1
+ WHEN 49 THEN 2
+ WHEN 50 THEN 3
+ WHEN 51 THEN 1
+ WHEN 52 THEN 2
+ WHEN 53 THEN 3
+ WHEN 54 THEN 1
+ WHEN 55 THEN 2
+ WHEN 56 THEN 3
+ WHEN 57 THEN 1
+ WHEN 58 THEN 2
+ WHEN 59 THEN 3
+ WHEN 60 THEN 1
+ WHEN 61 THEN 2
+ WHEN 62 THEN 3
+ WHEN 63 THEN 1
+ WHEN 64 THEN 2
+ WHEN 65 THEN 3
+ WHEN 66 THEN 1
+ WHEN 67 THEN 2
+ WHEN 68 THEN 3
+ WHEN 69 THEN 1
+ WHEN 70 THEN 2
+ WHEN 71 THEN 3
+ WHEN 72 THEN 1
+ WHEN 73 THEN 2
+ WHEN 74 THEN 3
+ WHEN 75 THEN 1
+ WHEN 76 THEN 2
+ WHEN 77 THEN 3
+ WHEN 78 THEN 1
+ WHEN 79 THEN 2
+ WHEN 80 THEN 3
+ WHEN 81 THEN 1
+ WHEN 82 THEN 2
+ WHEN 83 THEN 3
+ WHEN 84 THEN 1
+ WHEN 85 THEN 2
+ WHEN 86 THEN 3
+ WHEN 87 THEN 1
+ WHEN 88 THEN 2
+ WHEN 89 THEN 3
+ WHEN 90 THEN 1
+ WHEN 91 THEN 2
+ WHEN 92 THEN 3
+ WHEN 93 THEN 1
+ WHEN 94 THEN 2
+ WHEN 95 THEN 3
+ WHEN 96 THEN 1
+ WHEN 97 THEN 2
+ WHEN 98 THEN 3
+ WHEN 99 THEN 1
+ WHEN 100 THEN 2
+ ELSE primaryStoreId
+ END
+WHERE id BETWEEN 15 AND 100;
+
+UPDATE users
+SET fullName = TRIM(CONCAT_WS(' ', firstName, lastName))
+WHERE (fullName IS NULL OR fullName = '')
+ AND ((firstName IS NOT NULL AND firstName <> '') OR (lastName IS NOT NULL AND lastName <> ''));
+
+UPDATE activityLog al
+LEFT JOIN users u ON u.id = al.userId
+LEFT JOIN storeLocation s ON s.storeId = al.storeId
+SET al.usernameSnapshot = COALESCE(al.usernameSnapshot, u.username),
+ al.fullNameSnapshot = COALESCE(al.fullNameSnapshot, COALESCE(NULLIF(u.fullName, ''), TRIM(CONCAT_WS(' ', u.firstName, u.lastName)))),
+ al.roleSnapshot = COALESCE(al.roleSnapshot, u.role),
+ al.storeNameSnapshot = COALESCE(al.storeNameSnapshot, s.storeName)
+WHERE al.usernameSnapshot IS NULL
+ OR al.fullNameSnapshot IS NULL
+ OR al.roleSnapshot IS NULL
+ OR (al.storeId IS NOT NULL AND al.storeNameSnapshot IS NULL);
+
+
+UPDATE users
+SET avatarUrl = REPLACE(avatarUrl, 'https://images.petshop.local/users/', '/uploads/avatars/')
+WHERE avatarUrl LIKE 'https://images.petshop.local/users/%';
+
+UPDATE pet
+SET imageUrl = REPLACE(imageUrl, 'https://images.petshop.local/pets/', '/uploads/pets/')
+WHERE imageUrl LIKE 'https://images.petshop.local/pets/%';
+
+UPDATE product
+SET imageUrl = REPLACE(imageUrl, 'https://images.petshop.local/products/', '/uploads/products/')
+WHERE imageUrl LIKE 'https://images.petshop.local/products/%';
+
+UPDATE storeLocation
+SET imageUrl = REPLACE(imageUrl, 'https://images.petshop.local/stores/', '/stores/')
+WHERE imageUrl LIKE 'https://images.petshop.local/stores/%';
diff --git a/backend/src/main/resources/db/migration/V3__seed_openrouter_bot.sql b/backend/src/main/resources/db/migration/V3__seed_openrouter_bot.sql
deleted file mode 100644
index cf32a653..00000000
--- a/backend/src/main/resources/db/migration/V3__seed_openrouter_bot.sql
+++ /dev/null
@@ -1,21 +0,0 @@
-INSERT INTO users (username, password, email, firstName, lastName, fullName, phone, avatarUrl, role, staffRole, primaryStoreId, loyaltyPoints, active, tokenVersion)
-SELECT 'ai.bot',
- '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq',
- 'bot@petshop.com',
- 'AI',
- 'Bot',
- 'AI Bot',
- '000-000-0000',
- 'https://images.petshop.local/users/bot.webp',
- 'STAFF',
- 'CUSTOMER_SERVICE',
- NULL,
- 0,
- 1,
- 0
-FROM DUAL
-WHERE NOT EXISTS (
- SELECT 1
- FROM users
- WHERE username = 'ai.bot'
-);
diff --git a/backend/src/main/resources/db/migration/V4__activity_log_updates.sql b/backend/src/main/resources/db/migration/V4__activity_log_updates.sql
deleted file mode 100644
index 03d00660..00000000
--- a/backend/src/main/resources/db/migration/V4__activity_log_updates.sql
+++ /dev/null
@@ -1,7 +0,0 @@
-ALTER TABLE activityLog
- ADD COLUMN usernameSnapshot VARCHAR(50) NULL,
- ADD COLUMN fullNameSnapshot VARCHAR(100) NULL,
- ADD COLUMN roleSnapshot VARCHAR(20) NULL,
- ADD COLUMN storeNameSnapshot VARCHAR(100) NULL;
-
-CREATE INDEX idx_activity_log_timestamp_id ON activityLog(logTimestamp, logId);
diff --git a/backend/src/main/resources/db/migration/V5__add_points_columns_to_sale.sql b/backend/src/main/resources/db/migration/V5__add_points_columns_to_sale.sql
deleted file mode 100644
index 288561ef..00000000
--- a/backend/src/main/resources/db/migration/V5__add_points_columns_to_sale.sql
+++ /dev/null
@@ -1,3 +0,0 @@
-ALTER TABLE sale
- ADD COLUMN pointsUsed INT NOT NULL DEFAULT 0,
- ADD COLUMN pointsDiscountAmount DECIMAL(10, 2) NOT NULL DEFAULT 0.00;
diff --git a/backend/src/main/resources/dev/final-target/final_target_schema.sql b/backend/src/main/resources/dev/final-target/final_target_schema.sql
index f515888e..8abf1beb 100644
--- a/backend/src/main/resources/dev/final-target/final_target_schema.sql
+++ b/backend/src/main/resources/dev/final-target/final_target_schema.sql
@@ -311,6 +311,10 @@ CREATE TABLE IF NOT EXISTS activityLog (
logId BIGINT AUTO_INCREMENT PRIMARY KEY,
userId BIGINT NOT NULL,
storeId BIGINT NULL,
+ usernameSnapshot VARCHAR(50) NULL,
+ fullNameSnapshot VARCHAR(100) NULL,
+ roleSnapshot VARCHAR(20) NULL,
+ storeNameSnapshot VARCHAR(100) NULL,
activity TEXT NOT NULL,
logTimestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_activity_log_user FOREIGN KEY (userId) REFERENCES users(id),
@@ -343,3 +347,4 @@ CREATE INDEX idx_cart_user ON cart(userId);
CREATE INDEX idx_conversation_customer ON conversation(customerId);
CREATE INDEX idx_conversation_staff ON conversation(staffId);
CREATE INDEX idx_activity_log_store ON activityLog(storeId);
+CREATE INDEX idx_activity_log_timestamp_id ON activityLog(logTimestamp, logId);
diff --git a/backend/src/main/resources/dev/final-target/final_target_seed.sql b/backend/src/main/resources/dev/final-target/final_target_seed.sql
index 447f1dad..dbd7296a 100644
--- a/backend/src/main/resources/dev/final-target/final_target_seed.sql
+++ b/backend/src/main/resources/dev/final-target/final_target_seed.sql
@@ -69,91 +69,91 @@ INSERT INTO users (id, username, password, email, firstName, lastName, fullName,
(13, 'chloe.martin', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'chloe.martin@petshop.com', 'Chloe', 'Martin', 'Chloe Martin', '403-710-0013', 'https://images.petshop.local/users/013.webp', 'STAFF', 'VETERINARY_TECH', 3, 0, 1, 0),
(14, 'owen.baker', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'owen.baker@petshop.com', 'Owen', 'Baker', 'Owen Baker', '403-710-0014', 'https://images.petshop.local/users/014.webp', 'STAFF', 'GROOMER', 3, 0, 1, 0),
(15, 'customer', '$2y$10$fgIlTHDYUOzvbczwdhQP7..YuAHr2cGODb9OBQJqole3AkiY4CGUq', 'customer@petshop.com', 'Test', 'Customer', 'Test Customer', '000-000-1002', 'https://images.petshop.local/users/015.webp', 'CUSTOMER', 'CUSTOMER', 1, 0, 1, 0),
-(16, 'alex.brown', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.brown@gmail.com', 'Alex', 'Brown', 'Alex Brown', '403-730-0016', 'https://images.petshop.local/users/016.webp', 'CUSTOMER', 'CUSTOMER', 2, 12, 1, 0),
-(17, 'alex.clark', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.clark@gmail.com', 'Alex', 'Clark', 'Alex Clark', '403-730-0017', 'https://images.petshop.local/users/017.webp', 'CUSTOMER', 'CUSTOMER', 3, 15, 1, 0),
-(18, 'alex.wilson', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.wilson@gmail.com', 'Alex', 'Wilson', 'Alex Wilson', '403-730-0018', 'https://images.petshop.local/users/018.webp', 'CUSTOMER', 'CUSTOMER', 1, 2, 1, 0),
-(19, 'alex.martinez', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.martinez@gmail.com', 'Alex', 'Martinez', 'Alex Martinez', '403-730-0019', 'https://images.petshop.local/users/019.webp', 'CUSTOMER', 'CUSTOMER', 2, 5, 1, 0),
-(20, 'alex.anderson', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.anderson@gmail.com', 'Alex', 'Anderson', 'Alex Anderson', '403-730-0020', 'https://images.petshop.local/users/020.webp', 'CUSTOMER', 'CUSTOMER', 3, 12, 1, 0),
-(21, 'alex.taylor', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.taylor@gmail.com', 'Alex', 'Taylor', 'Alex Taylor', '403-730-0021', 'https://images.petshop.local/users/021.webp', 'CUSTOMER', 'CUSTOMER', 1, 11, 1, 0),
-(22, 'alex.parker', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.parker@gmail.com', 'Alex', 'Parker', 'Alex Parker', '403-730-0022', 'https://images.petshop.local/users/022.webp', 'CUSTOMER', 'CUSTOMER', 2, 16, 1, 0),
-(23, 'alex.evans', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.evans@gmail.com', 'Alex', 'Evans', 'Alex Evans', '403-730-0023', 'https://images.petshop.local/users/023.webp', 'CUSTOMER', 'CUSTOMER', 3, 36, 1, 0),
-(24, 'alex.scott', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.scott@gmail.com', 'Alex', 'Scott', 'Alex Scott', '403-730-0024', 'https://images.petshop.local/users/024.webp', 'CUSTOMER', 'CUSTOMER', 1, 5, 1, 0),
-(25, 'alex.adams', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.adams@gmail.com', 'Alex', 'Adams', 'Alex Adams', '403-730-0025', 'https://images.petshop.local/users/025.webp', 'CUSTOMER', 'CUSTOMER', 2, 8, 1, 0),
-(26, 'alex.baker', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.baker@gmail.com', 'Alex', 'Baker', 'Alex Baker', '403-730-0026', 'https://images.petshop.local/users/026.webp', 'CUSTOMER', 'CUSTOMER', 3, 29, 1, 0),
-(27, 'alex.hall', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.hall@gmail.com', 'Alex', 'Hall', 'Alex Hall', '403-730-0027', 'https://images.petshop.local/users/027.webp', 'CUSTOMER', 'CUSTOMER', 1, 3, 1, 0),
-(28, 'alex.rivera', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.rivera@gmail.com', 'Alex', 'Rivera', 'Alex Rivera', '403-730-0028', 'https://images.petshop.local/users/028.webp', 'CUSTOMER', 'CUSTOMER', 2, 13, 1, 0),
-(29, 'alex.mitchell', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.mitchell@gmail.com', 'Alex', 'Mitchell', 'Alex Mitchell', '403-730-0029', 'https://images.petshop.local/users/029.webp', 'CUSTOMER', 'CUSTOMER', 3, 30, 1, 0),
-(30, 'alex.collins', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.collins@gmail.com', 'Alex', 'Collins', 'Alex Collins', '403-730-0030', 'https://images.petshop.local/users/030.webp', 'CUSTOMER', 'CUSTOMER', 1, 16, 1, 0),
-(31, 'alex.morris', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.morris@gmail.com', 'Alex', 'Morris', 'Alex Morris', '403-730-0031', 'https://images.petshop.local/users/031.webp', 'CUSTOMER', 'CUSTOMER', 2, 9, 1, 0),
-(32, 'alex.cook', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.cook@gmail.com', 'Alex', 'Cook', 'Alex Cook', '403-730-0032', 'https://images.petshop.local/users/032.webp', 'CUSTOMER', 'CUSTOMER', 3, 19, 1, 0),
-(33, 'alex.bell', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.bell@gmail.com', 'Alex', 'Bell', 'Alex Bell', '403-730-0033', 'https://images.petshop.local/users/033.webp', 'CUSTOMER', 'CUSTOMER', 1, 2, 1, 0),
-(34, 'alex.reed', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.reed@gmail.com', 'Alex', 'Reed', 'Alex Reed', '403-730-0034', 'https://images.petshop.local/users/034.webp', 'CUSTOMER', 'CUSTOMER', 2, 5, 1, 0),
-(35, 'alex.murphy', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.murphy@gmail.com', 'Alex', 'Murphy', 'Alex Murphy', '403-730-0035', 'https://images.petshop.local/users/035.webp', 'CUSTOMER', 'CUSTOMER', 3, 31, 1, 0),
-(36, 'alex.bailey', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.bailey@gmail.com', 'Alex', 'Bailey', 'Alex Bailey', '403-730-0036', 'https://images.petshop.local/users/036.webp', 'CUSTOMER', 'CUSTOMER', 1, 6, 1, 0),
-(37, 'alex.cooper', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.cooper@gmail.com', 'Alex', 'Cooper', 'Alex Cooper', '403-730-0037', 'https://images.petshop.local/users/037.webp', 'CUSTOMER', 'CUSTOMER', 2, 4, 1, 0),
-(38, 'alex.richardson', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.richardson@gmail.com', 'Alex', 'Richardson', 'Alex Richardson', '403-730-0038', 'https://images.petshop.local/users/038.webp', 'CUSTOMER', 'CUSTOMER', 3, 19, 1, 0),
-(39, 'alex.cox', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.cox@gmail.com', 'Alex', 'Cox', 'Alex Cox', '403-730-0039', 'https://images.petshop.local/users/039.webp', 'CUSTOMER', 'CUSTOMER', 1, 4, 1, 0),
-(40, 'alex.howard', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.howard@gmail.com', 'Alex', 'Howard', 'Alex Howard', '403-730-0040', 'https://images.petshop.local/users/040.webp', 'CUSTOMER', 'CUSTOMER', 2, 12, 1, 0),
-(41, 'alex.ward', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.ward@gmail.com', 'Alex', 'Ward', 'Alex Ward', '403-730-0041', 'https://images.petshop.local/users/041.webp', 'CUSTOMER', 'CUSTOMER', 3, 18, 1, 0),
-(42, 'alex.torres', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.torres@gmail.com', 'Alex', 'Torres', 'Alex Torres', '403-730-0042', 'https://images.petshop.local/users/042.webp', 'CUSTOMER', 'CUSTOMER', 1, 10, 1, 0),
-(43, 'alex.peterson', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.peterson@gmail.com', 'Alex', 'Peterson', 'Alex Peterson', '403-730-0043', 'https://images.petshop.local/users/043.webp', 'CUSTOMER', 'CUSTOMER', 2, 6, 1, 0),
-(44, 'alex.gray', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.gray@gmail.com', 'Alex', 'Gray', 'Alex Gray', '403-730-0044', 'https://images.petshop.local/users/044.webp', 'CUSTOMER', 'CUSTOMER', 3, 11, 1, 0),
-(45, 'alex.ramirez', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.ramirez@gmail.com', 'Alex', 'Ramirez', 'Alex Ramirez', '403-730-0045', 'https://images.petshop.local/users/045.webp', 'CUSTOMER', 'CUSTOMER', 1, 5, 1, 0),
-(46, 'alex.james', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.james@gmail.com', 'Alex', 'James', 'Alex James', '403-730-0046', 'https://images.petshop.local/users/046.webp', 'CUSTOMER', 'CUSTOMER', 2, 28, 1, 0),
-(47, 'alex.watson', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.watson@gmail.com', 'Alex', 'Watson', 'Alex Watson', '403-730-0047', 'https://images.petshop.local/users/047.webp', 'CUSTOMER', 'CUSTOMER', 3, 8, 1, 0),
-(48, 'alex.brooks', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.brooks@gmail.com', 'Alex', 'Brooks', 'Alex Brooks', '403-730-0048', 'https://images.petshop.local/users/048.webp', 'CUSTOMER', 'CUSTOMER', 1, 2, 1, 0),
-(49, 'alex.kelly', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.kelly@gmail.com', 'Alex', 'Kelly', 'Alex Kelly', '403-730-0049', 'https://images.petshop.local/users/049.webp', 'CUSTOMER', 'CUSTOMER', 2, 16, 1, 0),
-(50, 'alex.sanders', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.sanders@gmail.com', 'Alex', 'Sanders', 'Alex Sanders', '403-730-0050', 'https://images.petshop.local/users/050.webp', 'CUSTOMER', 'CUSTOMER', 3, 21, 1, 0),
-(51, 'alex.price', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.price@gmail.com', 'Alex', 'Price', 'Alex Price', '403-730-0051', 'https://images.petshop.local/users/051.webp', 'CUSTOMER', 'CUSTOMER', 1, 7, 1, 0),
-(52, 'alex.bennett', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.bennett@gmail.com', 'Alex', 'Bennett', 'Alex Bennett', '403-730-0052', 'https://images.petshop.local/users/052.webp', 'CUSTOMER', 'CUSTOMER', 2, 17, 1, 0),
-(53, 'alex.wood', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.wood@gmail.com', 'Alex', 'Wood', 'Alex Wood', '403-730-0053', 'https://images.petshop.local/users/053.webp', 'CUSTOMER', 'CUSTOMER', 3, 10, 1, 0),
-(54, 'alex.barnes', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.barnes@gmail.com', 'Alex', 'Barnes', 'Alex Barnes', '403-730-0054', 'https://images.petshop.local/users/054.webp', 'CUSTOMER', 'CUSTOMER', 1, 2, 1, 0),
-(55, 'alex.ross', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.ross@gmail.com', 'Alex', 'Ross', 'Alex Ross', '403-730-0055', 'https://images.petshop.local/users/055.webp', 'CUSTOMER', 'CUSTOMER', 2, 7, 1, 0),
-(56, 'alex.henderson', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.henderson@gmail.com', 'Alex', 'Henderson', 'Alex Henderson', '403-730-0056', 'https://images.petshop.local/users/056.webp', 'CUSTOMER', 'CUSTOMER', 3, 15, 1, 0),
-(57, 'alex.coleman', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.coleman@gmail.com', 'Alex', 'Coleman', 'Alex Coleman', '403-730-0057', 'https://images.petshop.local/users/057.webp', 'CUSTOMER', 'CUSTOMER', 1, 2, 1, 0),
-(58, 'alex.jenkins', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.jenkins@gmail.com', 'Alex', 'Jenkins', 'Alex Jenkins', '403-730-0058', 'https://images.petshop.local/users/058.webp', 'CUSTOMER', 'CUSTOMER', 2, 17, 1, 0),
-(59, 'alex.perry', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.perry@gmail.com', 'Alex', 'Perry', 'Alex Perry', '403-730-0059', 'https://images.petshop.local/users/059.webp', 'CUSTOMER', 'CUSTOMER', 3, 15, 1, 0),
-(60, 'alex.powell', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.powell@gmail.com', 'Alex', 'Powell', 'Alex Powell', '403-730-0060', 'https://images.petshop.local/users/060.webp', 'CUSTOMER', 'CUSTOMER', 1, 4, 1, 0),
-(61, 'alex.long', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.long@gmail.com', 'Alex', 'Long', 'Alex Long', '403-730-0061', 'https://images.petshop.local/users/061.webp', 'CUSTOMER', 'CUSTOMER', 2, 13, 1, 0),
-(62, 'alex.patterson', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.patterson@gmail.com', 'Alex', 'Patterson', 'Alex Patterson', '403-730-0062', 'https://images.petshop.local/users/062.webp', 'CUSTOMER', 'CUSTOMER', 3, 26, 1, 0),
-(63, 'alex.hughes', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.hughes@gmail.com', 'Alex', 'Hughes', 'Alex Hughes', '403-730-0063', 'https://images.petshop.local/users/063.webp', 'CUSTOMER', 'CUSTOMER', 1, 5, 1, 0),
-(64, 'alex.flores', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.flores@gmail.com', 'Alex', 'Flores', 'Alex Flores', '403-730-0064', 'https://images.petshop.local/users/064.webp', 'CUSTOMER', 'CUSTOMER', 2, 9, 1, 0),
-(65, 'alex.washington', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.washington@gmail.com', 'Alex', 'Washington', 'Alex Washington', '403-730-0065', 'https://images.petshop.local/users/065.webp', 'CUSTOMER', 'CUSTOMER', 3, 22, 1, 0),
-(66, 'alex.butler', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.butler@gmail.com', 'Alex', 'Butler', 'Alex Butler', '403-730-0066', 'https://images.petshop.local/users/066.webp', 'CUSTOMER', 'CUSTOMER', 1, 5, 1, 0),
-(67, 'alex.simmons', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.simmons@gmail.com', 'Alex', 'Simmons', 'Alex Simmons', '403-730-0067', 'https://images.petshop.local/users/067.webp', 'CUSTOMER', 'CUSTOMER', 2, 5, 1, 0),
-(68, 'alex.foster', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.foster@gmail.com', 'Alex', 'Foster', 'Alex Foster', '403-730-0068', 'https://images.petshop.local/users/068.webp', 'CUSTOMER', 'CUSTOMER', 3, 17, 1, 0),
-(69, 'alex.gonzales', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.gonzales@gmail.com', 'Alex', 'Gonzales', 'Alex Gonzales', '403-730-0069', 'https://images.petshop.local/users/069.webp', 'CUSTOMER', 'CUSTOMER', 1, 15, 1, 0),
-(70, 'alex.bryant', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.bryant@gmail.com', 'Alex', 'Bryant', 'Alex Bryant', '403-730-0070', 'https://images.petshop.local/users/070.webp', 'CUSTOMER', 'CUSTOMER', 2, 19, 1, 0),
-(71, 'alex.alexander', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.alexander@gmail.com', 'Alex', 'Alexander', 'Alex Alexander', '403-730-0071', 'https://images.petshop.local/users/071.webp', 'CUSTOMER', 'CUSTOMER', 3, 13, 1, 0),
-(72, 'alex.russell', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.russell@gmail.com', 'Alex', 'Russell', 'Alex Russell', '403-730-0072', 'https://images.petshop.local/users/072.webp', 'CUSTOMER', 'CUSTOMER', 1, 7, 1, 0),
-(73, 'alex.griffin', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.griffin@gmail.com', 'Alex', 'Griffin', 'Alex Griffin', '403-730-0073', 'https://images.petshop.local/users/073.webp', 'CUSTOMER', 'CUSTOMER', 2, 2, 1, 0),
-(74, 'alex.diaz', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.diaz@gmail.com', 'Alex', 'Diaz', 'Alex Diaz', '403-730-0074', 'https://images.petshop.local/users/074.webp', 'CUSTOMER', 'CUSTOMER', 3, 10, 1, 0),
-(75, 'alex.hayes', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.hayes@gmail.com', 'Alex', 'Hayes', 'Alex Hayes', '403-730-0075', 'https://images.petshop.local/users/075.webp', 'CUSTOMER', 'CUSTOMER', 1, 7, 1, 0),
-(76, 'alex.myers', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.myers@gmail.com', 'Alex', 'Myers', 'Alex Myers', '403-730-0076', 'https://images.petshop.local/users/076.webp', 'CUSTOMER', 'CUSTOMER', 2, 13, 1, 0),
-(77, 'alex.ford', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.ford@gmail.com', 'Alex', 'Ford', 'Alex Ford', '403-730-0077', 'https://images.petshop.local/users/077.webp', 'CUSTOMER', 'CUSTOMER', 3, 13, 1, 0),
-(78, 'alex.hamilton', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.hamilton@gmail.com', 'Alex', 'Hamilton', 'Alex Hamilton', '403-730-0078', 'https://images.petshop.local/users/078.webp', 'CUSTOMER', 'CUSTOMER', 1, 2, 1, 0),
-(79, 'alex.graham', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.graham@gmail.com', 'Alex', 'Graham', 'Alex Graham', '403-730-0079', 'https://images.petshop.local/users/079.webp', 'CUSTOMER', 'CUSTOMER', 2, 5, 1, 0),
-(80, 'alex.sullivan', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.sullivan@gmail.com', 'Alex', 'Sullivan', 'Alex Sullivan', '403-730-0080', 'https://images.petshop.local/users/080.webp', 'CUSTOMER', 'CUSTOMER', 3, 12, 1, 0),
-(81, 'alex.wallace', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.wallace@gmail.com', 'Alex', 'Wallace', 'Alex Wallace', '403-730-0081', 'https://images.petshop.local/users/081.webp', 'CUSTOMER', 'CUSTOMER', 1, 11, 1, 0),
-(82, 'alex.woods', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.woods@gmail.com', 'Alex', 'Woods', 'Alex Woods', '403-730-0082', 'https://images.petshop.local/users/082.webp', 'CUSTOMER', 'CUSTOMER', 2, 17, 1, 0),
-(83, 'alex.cole', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.cole@gmail.com', 'Alex', 'Cole', 'Alex Cole', '403-730-0083', 'https://images.petshop.local/users/083.webp', 'CUSTOMER', 'CUSTOMER', 3, 36, 1, 0),
-(84, 'alex.west', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.west@gmail.com', 'Alex', 'West', 'Alex West', '403-730-0084', 'https://images.petshop.local/users/084.webp', 'CUSTOMER', 'CUSTOMER', 1, 5, 1, 0),
-(85, 'alex.jordan', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.jordan@gmail.com', 'Alex', 'Jordan', 'Alex Jordan', '403-730-0085', 'https://images.petshop.local/users/085.webp', 'CUSTOMER', 'CUSTOMER', 2, 9, 1, 0),
-(86, 'alex.owens', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.owens@gmail.com', 'Alex', 'Owens', 'Alex Owens', '403-730-0086', 'https://images.petshop.local/users/086.webp', 'CUSTOMER', 'CUSTOMER', 3, 26, 1, 0),
-(87, 'alex.reynolds', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.reynolds@gmail.com', 'Alex', 'Reynolds', 'Alex Reynolds', '403-730-0087', 'https://images.petshop.local/users/087.webp', 'CUSTOMER', 'CUSTOMER', 1, 3, 1, 0),
-(88, 'alex.fisher', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.fisher@gmail.com', 'Alex', 'Fisher', 'Alex Fisher', '403-730-0088', 'https://images.petshop.local/users/088.webp', 'CUSTOMER', 'CUSTOMER', 2, 11, 1, 0),
-(89, 'alex.ellis', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.ellis@gmail.com', 'Alex', 'Ellis', 'Alex Ellis', '403-730-0089', 'https://images.petshop.local/users/089.webp', 'CUSTOMER', 'CUSTOMER', 3, 30, 1, 0),
-(90, 'alex.harrison', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.harrison@gmail.com', 'Alex', 'Harrison', 'Alex Harrison', '403-730-0090', 'https://images.petshop.local/users/090.webp', 'CUSTOMER', 'CUSTOMER', 1, 16, 1, 0),
-(91, 'alex.gibson', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.gibson@gmail.com', 'Alex', 'Gibson', 'Alex Gibson', '403-730-0091', 'https://images.petshop.local/users/091.webp', 'CUSTOMER', 'CUSTOMER', 2, 9, 1, 0),
-(92, 'alex.mcdonald', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.mcdonald@gmail.com', 'Alex', 'Mcdonald', 'Alex Mcdonald', '403-730-0092', 'https://images.petshop.local/users/092.webp', 'CUSTOMER', 'CUSTOMER', 3, 19, 1, 0),
-(93, 'alex.cruz', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.cruz@gmail.com', 'Alex', 'Cruz', 'Alex Cruz', '403-730-0093', 'https://images.petshop.local/users/093.webp', 'CUSTOMER', 'CUSTOMER', 1, 2, 1, 0),
-(94, 'alex.marshall', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.marshall@gmail.com', 'Alex', 'Marshall', 'Alex Marshall', '403-730-0094', 'https://images.petshop.local/users/094.webp', 'CUSTOMER', 'CUSTOMER', 2, 5, 1, 0),
-(95, 'alex.ortiz', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.ortiz@gmail.com', 'Alex', 'Ortiz', 'Alex Ortiz', '403-730-0095', 'https://images.petshop.local/users/095.webp', 'CUSTOMER', 'CUSTOMER', 3, 30, 1, 0),
-(96, 'alex.gomez', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.gomez@gmail.com', 'Alex', 'Gomez', 'Alex Gomez', '403-730-0096', 'https://images.petshop.local/users/096.webp', 'CUSTOMER', 'CUSTOMER', 1, 6, 1, 0),
-(97, 'alex.murray', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.murray@gmail.com', 'Alex', 'Murray', 'Alex Murray', '403-730-0097', 'https://images.petshop.local/users/097.webp', 'CUSTOMER', 'CUSTOMER', 2, 4, 1, 0),
-(98, 'alex.freeman', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.freeman@gmail.com', 'Alex', 'Freeman', 'Alex Freeman', '403-730-0098', 'https://images.petshop.local/users/098.webp', 'CUSTOMER', 'CUSTOMER', 3, 0, 1, 0),
-(99, 'alex.wells', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.wells@gmail.com', 'Alex', 'Wells', 'Alex Wells', '403-730-0099', 'https://images.petshop.local/users/099.webp', 'CUSTOMER', 'CUSTOMER', 1, 0, 1, 0),
-(100, 'alex.webb', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alex.webb@gmail.com', 'Alex', 'Webb', 'Alex Webb', '403-730-0100', 'https://images.petshop.local/users/100.webp', 'CUSTOMER', 'CUSTOMER', 2, 0, 1, 0),
+(16, 'maya.brown', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'maya.brown@gmail.com', 'Maya', 'Brown', 'Maya Brown', '403-730-0016', 'https://images.petshop.local/users/016.webp', 'CUSTOMER', 'CUSTOMER', 2, 12, 1, 0),
+(17, 'noah.clark', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'noah.clark@gmail.com', 'Noah', 'Clark', 'Noah Clark', '403-730-0017', 'https://images.petshop.local/users/017.webp', 'CUSTOMER', 'CUSTOMER', 3, 15, 1, 0),
+(18, 'avery.wilson', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'avery.wilson@gmail.com', 'Avery', 'Wilson', 'Avery Wilson', '403-730-0018', 'https://images.petshop.local/users/018.webp', 'CUSTOMER', 'CUSTOMER', 1, 2, 1, 0),
+(19, 'leah.martinez', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'leah.martinez@gmail.com', 'Leah', 'Martinez', 'Leah Martinez', '403-730-0019', 'https://images.petshop.local/users/019.webp', 'CUSTOMER', 'CUSTOMER', 2, 5, 1, 0),
+(20, 'julian.anderson', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'julian.anderson@gmail.com', 'Julian', 'Anderson', 'Julian Anderson', '403-730-0020', 'https://images.petshop.local/users/020.webp', 'CUSTOMER', 'CUSTOMER', 3, 12, 1, 0),
+(21, 'zoe.taylor', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'zoe.taylor@gmail.com', 'Zoe', 'Taylor', 'Zoe Taylor', '403-730-0021', 'https://images.petshop.local/users/021.webp', 'CUSTOMER', 'CUSTOMER', 1, 11, 1, 0),
+(22, 'ethan.parker', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'ethan.parker@gmail.com', 'Ethan', 'Parker', 'Ethan Parker', '403-730-0022', 'https://images.petshop.local/users/022.webp', 'CUSTOMER', 'CUSTOMER', 2, 16, 1, 0),
+(23, 'ruby.evans', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'ruby.evans@gmail.com', 'Ruby', 'Evans', 'Ruby Evans', '403-730-0023', 'https://images.petshop.local/users/023.webp', 'CUSTOMER', 'CUSTOMER', 3, 36, 1, 0),
+(24, 'caleb.scott', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'caleb.scott@gmail.com', 'Caleb', 'Scott', 'Caleb Scott', '403-730-0024', 'https://images.petshop.local/users/024.webp', 'CUSTOMER', 'CUSTOMER', 1, 5, 1, 0),
+(25, 'ivy.adams', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'ivy.adams@gmail.com', 'Ivy', 'Adams', 'Ivy Adams', '403-730-0025', 'https://images.petshop.local/users/025.webp', 'CUSTOMER', 'CUSTOMER', 2, 8, 1, 0),
+(26, 'isaac.baker', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'isaac.baker@gmail.com', 'Isaac', 'Baker', 'Isaac Baker', '403-730-0026', 'https://images.petshop.local/users/026.webp', 'CUSTOMER', 'CUSTOMER', 3, 29, 1, 0),
+(27, 'hannah.hall', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'hannah.hall@gmail.com', 'Hannah', 'Hall', 'Hannah Hall', '403-730-0027', 'https://images.petshop.local/users/027.webp', 'CUSTOMER', 'CUSTOMER', 1, 3, 1, 0),
+(28, 'mason.rivera', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'mason.rivera@gmail.com', 'Mason', 'Rivera', 'Mason Rivera', '403-730-0028', 'https://images.petshop.local/users/028.webp', 'CUSTOMER', 'CUSTOMER', 2, 13, 1, 0),
+(29, 'aria.mitchell', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'aria.mitchell@gmail.com', 'Aria', 'Mitchell', 'Aria Mitchell', '403-730-0029', 'https://images.petshop.local/users/029.webp', 'CUSTOMER', 'CUSTOMER', 3, 30, 1, 0),
+(30, 'wyatt.collins', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'wyatt.collins@gmail.com', 'Wyatt', 'Collins', 'Wyatt Collins', '403-730-0030', 'https://images.petshop.local/users/030.webp', 'CUSTOMER', 'CUSTOMER', 1, 16, 1, 0),
+(31, 'elena.morris', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'elena.morris@gmail.com', 'Elena', 'Morris', 'Elena Morris', '403-730-0031', 'https://images.petshop.local/users/031.webp', 'CUSTOMER', 'CUSTOMER', 2, 9, 1, 0),
+(32, 'leo.cook', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'leo.cook@gmail.com', 'Leo', 'Cook', 'Leo Cook', '403-730-0032', 'https://images.petshop.local/users/032.webp', 'CUSTOMER', 'CUSTOMER', 3, 19, 1, 0),
+(33, 'grace.bell', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'grace.bell@gmail.com', 'Grace', 'Bell', 'Grace Bell', '403-730-0033', 'https://images.petshop.local/users/033.webp', 'CUSTOMER', 'CUSTOMER', 1, 2, 1, 0),
+(34, 'hudson.reed', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'hudson.reed@gmail.com', 'Hudson', 'Reed', 'Hudson Reed', '403-730-0034', 'https://images.petshop.local/users/034.webp', 'CUSTOMER', 'CUSTOMER', 2, 5, 1, 0),
+(35, 'claire.murphy', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'claire.murphy@gmail.com', 'Claire', 'Murphy', 'Claire Murphy', '403-730-0035', 'https://images.petshop.local/users/035.webp', 'CUSTOMER', 'CUSTOMER', 3, 31, 1, 0),
+(36, 'omar.bailey', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'omar.bailey@gmail.com', 'Omar', 'Bailey', 'Omar Bailey', '403-730-0036', 'https://images.petshop.local/users/036.webp', 'CUSTOMER', 'CUSTOMER', 1, 6, 1, 0),
+(37, 'naomi.cooper', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'naomi.cooper@gmail.com', 'Naomi', 'Cooper', 'Naomi Cooper', '403-730-0037', 'https://images.petshop.local/users/037.webp', 'CUSTOMER', 'CUSTOMER', 2, 4, 1, 0),
+(38, 'jasper.richardson', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'jasper.richardson@gmail.com', 'Jasper', 'Richardson', 'Jasper Richardson', '403-730-0038', 'https://images.petshop.local/users/038.webp', 'CUSTOMER', 'CUSTOMER', 3, 19, 1, 0),
+(39, 'sofia.cox', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'sofia.cox@gmail.com', 'Sofia', 'Cox', 'Sofia Cox', '403-730-0039', 'https://images.petshop.local/users/039.webp', 'CUSTOMER', 'CUSTOMER', 1, 4, 1, 0),
+(40, 'miles.howard', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'miles.howard@gmail.com', 'Miles', 'Howard', 'Miles Howard', '403-730-0040', 'https://images.petshop.local/users/040.webp', 'CUSTOMER', 'CUSTOMER', 2, 12, 1, 0),
+(41, 'audrey.ward', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'audrey.ward@gmail.com', 'Audrey', 'Ward', 'Audrey Ward', '403-730-0041', 'https://images.petshop.local/users/041.webp', 'CUSTOMER', 'CUSTOMER', 3, 18, 1, 0),
+(42, 'nathan.torres', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'nathan.torres@gmail.com', 'Nathan', 'Torres', 'Nathan Torres', '403-730-0042', 'https://images.petshop.local/users/042.webp', 'CUSTOMER', 'CUSTOMER', 1, 10, 1, 0),
+(43, 'jade.peterson', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'jade.peterson@gmail.com', 'Jade', 'Peterson', 'Jade Peterson', '403-730-0043', 'https://images.petshop.local/users/043.webp', 'CUSTOMER', 'CUSTOMER', 2, 6, 1, 0),
+(44, 'rowan.gray', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'rowan.gray@gmail.com', 'Rowan', 'Gray', 'Rowan Gray', '403-730-0044', 'https://images.petshop.local/users/044.webp', 'CUSTOMER', 'CUSTOMER', 3, 11, 1, 0),
+(45, 'lila.ramirez', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'lila.ramirez@gmail.com', 'Lila', 'Ramirez', 'Lila Ramirez', '403-730-0045', 'https://images.petshop.local/users/045.webp', 'CUSTOMER', 'CUSTOMER', 1, 5, 1, 0),
+(46, 'eli.james', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'eli.james@gmail.com', 'Eli', 'James', 'Eli James', '403-730-0046', 'https://images.petshop.local/users/046.webp', 'CUSTOMER', 'CUSTOMER', 2, 28, 1, 0),
+(47, 'violet.watson', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'violet.watson@gmail.com', 'Violet', 'Watson', 'Violet Watson', '403-730-0047', 'https://images.petshop.local/users/047.webp', 'CUSTOMER', 'CUSTOMER', 3, 8, 1, 0),
+(48, 'gavin.brooks', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'gavin.brooks@gmail.com', 'Gavin', 'Brooks', 'Gavin Brooks', '403-730-0048', 'https://images.petshop.local/users/048.webp', 'CUSTOMER', 'CUSTOMER', 1, 2, 1, 0),
+(49, 'stella.kelly', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'stella.kelly@gmail.com', 'Stella', 'Kelly', 'Stella Kelly', '403-730-0049', 'https://images.petshop.local/users/049.webp', 'CUSTOMER', 'CUSTOMER', 2, 16, 1, 0),
+(50, 'adrian.sanders', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'adrian.sanders@gmail.com', 'Adrian', 'Sanders', 'Adrian Sanders', '403-730-0050', 'https://images.petshop.local/users/050.webp', 'CUSTOMER', 'CUSTOMER', 3, 21, 1, 0),
+(51, 'hazel.price', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'hazel.price@gmail.com', 'Hazel', 'Price', 'Hazel Price', '403-730-0051', 'https://images.petshop.local/users/051.webp', 'CUSTOMER', 'CUSTOMER', 1, 7, 1, 0),
+(52, 'connor.bennett', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'connor.bennett@gmail.com', 'Connor', 'Bennett', 'Connor Bennett', '403-730-0052', 'https://images.petshop.local/users/052.webp', 'CUSTOMER', 'CUSTOMER', 2, 17, 1, 0),
+(53, 'sadie.wood', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'sadie.wood@gmail.com', 'Sadie', 'Wood', 'Sadie Wood', '403-730-0053', 'https://images.petshop.local/users/053.webp', 'CUSTOMER', 'CUSTOMER', 3, 10, 1, 0),
+(54, 'xavier.barnes', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'xavier.barnes@gmail.com', 'Xavier', 'Barnes', 'Xavier Barnes', '403-730-0054', 'https://images.petshop.local/users/054.webp', 'CUSTOMER', 'CUSTOMER', 1, 2, 1, 0),
+(55, 'alice.ross', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'alice.ross@gmail.com', 'Alice', 'Ross', 'Alice Ross', '403-730-0055', 'https://images.petshop.local/users/055.webp', 'CUSTOMER', 'CUSTOMER', 2, 7, 1, 0),
+(56, 'roman.henderson', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'roman.henderson@gmail.com', 'Roman', 'Henderson', 'Roman Henderson', '403-730-0056', 'https://images.petshop.local/users/056.webp', 'CUSTOMER', 'CUSTOMER', 3, 15, 1, 0),
+(57, 'lucy.coleman', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'lucy.coleman@gmail.com', 'Lucy', 'Coleman', 'Lucy Coleman', '403-730-0057', 'https://images.petshop.local/users/057.webp', 'CUSTOMER', 'CUSTOMER', 1, 2, 1, 0),
+(58, 'evan.jenkins', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'evan.jenkins@gmail.com', 'Evan', 'Jenkins', 'Evan Jenkins', '403-730-0058', 'https://images.petshop.local/users/058.webp', 'CUSTOMER', 'CUSTOMER', 2, 17, 1, 0),
+(59, 'mila.perry', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'mila.perry@gmail.com', 'Mila', 'Perry', 'Mila Perry', '403-730-0059', 'https://images.petshop.local/users/059.webp', 'CUSTOMER', 'CUSTOMER', 3, 15, 1, 0),
+(60, 'cole.powell', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'cole.powell@gmail.com', 'Cole', 'Powell', 'Cole Powell', '403-730-0060', 'https://images.petshop.local/users/060.webp', 'CUSTOMER', 'CUSTOMER', 1, 4, 1, 0),
+(61, 'nora.long', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'nora.long@gmail.com', 'Nora', 'Long', 'Nora Long', '403-730-0061', 'https://images.petshop.local/users/061.webp', 'CUSTOMER', 'CUSTOMER', 2, 13, 1, 0),
+(62, 'adam.patterson', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'adam.patterson@gmail.com', 'Adam', 'Patterson', 'Adam Patterson', '403-730-0062', 'https://images.petshop.local/users/062.webp', 'CUSTOMER', 'CUSTOMER', 3, 26, 1, 0),
+(63, 'layla.hughes', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'layla.hughes@gmail.com', 'Layla', 'Hughes', 'Layla Hughes', '403-730-0063', 'https://images.petshop.local/users/063.webp', 'CUSTOMER', 'CUSTOMER', 1, 5, 1, 0),
+(64, 'blake.flores', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'blake.flores@gmail.com', 'Blake', 'Flores', 'Blake Flores', '403-730-0064', 'https://images.petshop.local/users/064.webp', 'CUSTOMER', 'CUSTOMER', 2, 9, 1, 0),
+(65, 'ellie.washington', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'ellie.washington@gmail.com', 'Ellie', 'Washington', 'Ellie Washington', '403-730-0065', 'https://images.petshop.local/users/065.webp', 'CUSTOMER', 'CUSTOMER', 3, 22, 1, 0),
+(66, 'ryan.butler', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'ryan.butler@gmail.com', 'Ryan', 'Butler', 'Ryan Butler', '403-730-0066', 'https://images.petshop.local/users/066.webp', 'CUSTOMER', 'CUSTOMER', 1, 5, 1, 0),
+(67, 'cora.simmons', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'cora.simmons@gmail.com', 'Cora', 'Simmons', 'Cora Simmons', '403-730-0067', 'https://images.petshop.local/users/067.webp', 'CUSTOMER', 'CUSTOMER', 2, 5, 1, 0),
+(68, 'simon.foster', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'simon.foster@gmail.com', 'Simon', 'Foster', 'Simon Foster', '403-730-0068', 'https://images.petshop.local/users/068.webp', 'CUSTOMER', 'CUSTOMER', 3, 17, 1, 0),
+(69, 'piper.gonzales', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'piper.gonzales@gmail.com', 'Piper', 'Gonzales', 'Piper Gonzales', '403-730-0069', 'https://images.petshop.local/users/069.webp', 'CUSTOMER', 'CUSTOMER', 1, 15, 1, 0),
+(70, 'joel.bryant', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'joel.bryant@gmail.com', 'Joel', 'Bryant', 'Joel Bryant', '403-730-0070', 'https://images.petshop.local/users/070.webp', 'CUSTOMER', 'CUSTOMER', 2, 19, 1, 0),
+(71, 'eva.alexander', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'eva.alexander@gmail.com', 'Eva', 'Alexander', 'Eva Alexander', '403-730-0071', 'https://images.petshop.local/users/071.webp', 'CUSTOMER', 'CUSTOMER', 3, 13, 1, 0),
+(72, 'felix.russell', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'felix.russell@gmail.com', 'Felix', 'Russell', 'Felix Russell', '403-730-0072', 'https://images.petshop.local/users/072.webp', 'CUSTOMER', 'CUSTOMER', 1, 7, 1, 0),
+(73, 'maeve.griffin', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'maeve.griffin@gmail.com', 'Maeve', 'Griffin', 'Maeve Griffin', '403-730-0073', 'https://images.petshop.local/users/073.webp', 'CUSTOMER', 'CUSTOMER', 2, 2, 1, 0),
+(74, 'tristan.diaz', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'tristan.diaz@gmail.com', 'Tristan', 'Diaz', 'Tristan Diaz', '403-730-0074', 'https://images.petshop.local/users/074.webp', 'CUSTOMER', 'CUSTOMER', 3, 10, 1, 0),
+(75, 'ariana.hayes', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'ariana.hayes@gmail.com', 'Ariana', 'Hayes', 'Ariana Hayes', '403-730-0075', 'https://images.petshop.local/users/075.webp', 'CUSTOMER', 'CUSTOMER', 1, 7, 1, 0),
+(76, 'declan.myers', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'declan.myers@gmail.com', 'Declan', 'Myers', 'Declan Myers', '403-730-0076', 'https://images.petshop.local/users/076.webp', 'CUSTOMER', 'CUSTOMER', 2, 13, 1, 0),
+(77, 'brooke.ford', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'brooke.ford@gmail.com', 'Brooke', 'Ford', 'Brooke Ford', '403-730-0077', 'https://images.petshop.local/users/077.webp', 'CUSTOMER', 'CUSTOMER', 3, 13, 1, 0),
+(78, 'micah.hamilton', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'micah.hamilton@gmail.com', 'Micah', 'Hamilton', 'Micah Hamilton', '403-730-0078', 'https://images.petshop.local/users/078.webp', 'CUSTOMER', 'CUSTOMER', 1, 2, 1, 0),
+(79, 'bianca.graham', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'bianca.graham@gmail.com', 'Bianca', 'Graham', 'Bianca Graham', '403-730-0079', 'https://images.petshop.local/users/079.webp', 'CUSTOMER', 'CUSTOMER', 2, 5, 1, 0),
+(80, 'jonah.sullivan', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'jonah.sullivan@gmail.com', 'Jonah', 'Sullivan', 'Jonah Sullivan', '403-730-0080', 'https://images.petshop.local/users/080.webp', 'CUSTOMER', 'CUSTOMER', 3, 12, 1, 0),
+(81, 'tessa.wallace', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'tessa.wallace@gmail.com', 'Tessa', 'Wallace', 'Tessa Wallace', '403-730-0081', 'https://images.petshop.local/users/081.webp', 'CUSTOMER', 'CUSTOMER', 1, 11, 1, 0),
+(82, 'damian.woods', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'damian.woods@gmail.com', 'Damian', 'Woods', 'Damian Woods', '403-730-0082', 'https://images.petshop.local/users/082.webp', 'CUSTOMER', 'CUSTOMER', 2, 17, 1, 0),
+(83, 'riley.cole', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'riley.cole@gmail.com', 'Riley', 'Cole', 'Riley Cole', '403-730-0083', 'https://images.petshop.local/users/083.webp', 'CUSTOMER', 'CUSTOMER', 3, 36, 1, 0),
+(84, 'kieran.west', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'kieran.west@gmail.com', 'Kieran', 'West', 'Kieran West', '403-730-0084', 'https://images.petshop.local/users/084.webp', 'CUSTOMER', 'CUSTOMER', 1, 5, 1, 0),
+(85, 'sienna.jordan', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'sienna.jordan@gmail.com', 'Sienna', 'Jordan', 'Sienna Jordan', '403-730-0085', 'https://images.petshop.local/users/085.webp', 'CUSTOMER', 'CUSTOMER', 2, 9, 1, 0),
+(86, 'finley.owens', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'finley.owens@gmail.com', 'Finley', 'Owens', 'Finley Owens', '403-730-0086', 'https://images.petshop.local/users/086.webp', 'CUSTOMER', 'CUSTOMER', 3, 26, 1, 0),
+(87, 'maren.reynolds', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'maren.reynolds@gmail.com', 'Maren', 'Reynolds', 'Maren Reynolds', '403-730-0087', 'https://images.petshop.local/users/087.webp', 'CUSTOMER', 'CUSTOMER', 1, 3, 1, 0),
+(88, 'asher.fisher', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'asher.fisher@gmail.com', 'Asher', 'Fisher', 'Asher Fisher', '403-730-0088', 'https://images.petshop.local/users/088.webp', 'CUSTOMER', 'CUSTOMER', 2, 11, 1, 0),
+(89, 'daphne.ellis', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'daphne.ellis@gmail.com', 'Daphne', 'Ellis', 'Daphne Ellis', '403-730-0089', 'https://images.petshop.local/users/089.webp', 'CUSTOMER', 'CUSTOMER', 3, 30, 1, 0),
+(90, 'bennett.harrison', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'bennett.harrison@gmail.com', 'Bennett', 'Harrison', 'Bennett Harrison', '403-730-0090', 'https://images.petshop.local/users/090.webp', 'CUSTOMER', 'CUSTOMER', 1, 16, 1, 0),
+(91, 'selena.gibson', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'selena.gibson@gmail.com', 'Selena', 'Gibson', 'Selena Gibson', '403-730-0091', 'https://images.petshop.local/users/091.webp', 'CUSTOMER', 'CUSTOMER', 2, 9, 1, 0),
+(92, 'emmett.mcdonald', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'emmett.mcdonald@gmail.com', 'Emmett', 'Mcdonald', 'Emmett Mcdonald', '403-730-0092', 'https://images.petshop.local/users/092.webp', 'CUSTOMER', 'CUSTOMER', 3, 19, 1, 0),
+(93, 'phoebe.cruz', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'phoebe.cruz@gmail.com', 'Phoebe', 'Cruz', 'Phoebe Cruz', '403-730-0093', 'https://images.petshop.local/users/093.webp', 'CUSTOMER', 'CUSTOMER', 1, 2, 1, 0),
+(94, 'sawyer.marshall', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'sawyer.marshall@gmail.com', 'Sawyer', 'Marshall', 'Sawyer Marshall', '403-730-0094', 'https://images.petshop.local/users/094.webp', 'CUSTOMER', 'CUSTOMER', 2, 5, 1, 0),
+(95, 'keira.ortiz', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'keira.ortiz@gmail.com', 'Keira', 'Ortiz', 'Keira Ortiz', '403-730-0095', 'https://images.petshop.local/users/095.webp', 'CUSTOMER', 'CUSTOMER', 3, 30, 1, 0),
+(96, 'landon.gomez', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'landon.gomez@gmail.com', 'Landon', 'Gomez', 'Landon Gomez', '403-730-0096', 'https://images.petshop.local/users/096.webp', 'CUSTOMER', 'CUSTOMER', 1, 6, 1, 0),
+(97, 'rosalie.murray', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'rosalie.murray@gmail.com', 'Rosalie', 'Murray', 'Rosalie Murray', '403-730-0097', 'https://images.petshop.local/users/097.webp', 'CUSTOMER', 'CUSTOMER', 2, 4, 1, 0),
+(98, 'malik.freeman', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'malik.freeman@gmail.com', 'Malik', 'Freeman', 'Malik Freeman', '403-730-0098', 'https://images.petshop.local/users/098.webp', 'CUSTOMER', 'CUSTOMER', 3, 0, 1, 0),
+(99, 'esme.wells', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'esme.wells@gmail.com', 'Esme', 'Wells', 'Esme Wells', '403-730-0099', 'https://images.petshop.local/users/099.webp', 'CUSTOMER', 'CUSTOMER', 1, 0, 1, 0),
+(100, 'holden.webb', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'holden.webb@gmail.com', 'Holden', 'Webb', 'Holden Webb', '403-730-0100', 'https://images.petshop.local/users/100.webp', 'CUSTOMER', 'CUSTOMER', 2, 0, 1, 0),
(101, 'ai.bot', '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq', 'bot@petshop.com', 'AI', 'Bot', 'AI Bot', '000-000-0000', 'https://images.petshop.local/users/bot.webp', 'STAFF', 'CUSTOMER_SERVICE', NULL, 0, 1, 0);
INSERT INTO supplier (supId, supCompany, supContactFirstName, supContactLastName, supEmail, supPhone) VALUES
@@ -1739,124 +1739,124 @@ INSERT INTO message (id, conversationId, senderId, content, attachmentUrl, attac
(119, 30, 45, 'Order #1030 is the one I meant, and the pet is Kiki.', NULL, NULL, NULL, NULL, '2026-03-02 09:10:00', 1),
(120, 30, 8, 'Thanks, the account and order are now updated on this conversation.', NULL, NULL, NULL, NULL, '2026-03-02 09:15:00', 0);
-INSERT INTO activityLog (logId, userId, storeId, activity, logTimestamp) VALUES
-(1, 1, 1, 'Reviewed store inventory adjustments.', '2026-01-03 08:00:00'),
-(2, 2, 2, 'Approved a purchase transaction at the register.', '2026-01-03 17:00:00'),
-(3, 3, 1, 'Updated a pet availability record.', '2026-01-04 02:00:00'),
-(4, 4, 1, 'Completed a grooming appointment handoff.', '2026-01-04 11:00:00'),
-(5, 5, 1, 'Checked a pending adoption record.', '2026-01-04 20:00:00'),
-(6, 6, 1, 'Reviewed a refund request tied to an original sale.', '2026-01-05 05:00:00'),
-(7, 7, 2, 'Answered a customer support conversation.', '2026-01-05 14:00:00'),
-(8, 8, 2, 'Updated a product detail for the catalogue.', '2026-01-05 23:00:00'),
-(9, 9, 2, 'Reviewed store inventory adjustments.', '2026-01-06 08:00:00'),
-(10, 10, 2, 'Approved a purchase transaction at the register.', '2026-01-06 17:00:00'),
-(11, 11, 3, 'Updated a pet availability record.', '2026-01-07 02:00:00'),
-(12, 12, 3, 'Completed a grooming appointment handoff.', '2026-01-07 11:00:00'),
-(13, 13, 3, 'Checked a pending adoption record.', '2026-01-07 20:00:00'),
-(14, 14, 3, 'Reviewed a refund request tied to an original sale.', '2026-01-08 05:00:00'),
-(15, 1, 1, 'Answered a customer support conversation.', '2026-01-08 14:00:00'),
-(16, 2, 2, 'Updated a product detail for the catalogue.', '2026-01-08 23:00:00'),
-(17, 3, 1, 'Reviewed store inventory adjustments.', '2026-01-09 08:00:00'),
-(18, 4, 1, 'Approved a purchase transaction at the register.', '2026-01-09 17:00:00'),
-(19, 5, 1, 'Updated a pet availability record.', '2026-01-10 02:00:00'),
-(20, 6, 1, 'Completed a grooming appointment handoff.', '2026-01-10 11:00:00'),
-(21, 7, 2, 'Checked a pending adoption record.', '2026-01-10 20:00:00'),
-(22, 8, 2, 'Reviewed a refund request tied to an original sale.', '2026-01-11 05:00:00'),
-(23, 9, 2, 'Answered a customer support conversation.', '2026-01-11 14:00:00'),
-(24, 10, 2, 'Updated a product detail for the catalogue.', '2026-01-11 23:00:00'),
-(25, 11, 3, 'Reviewed store inventory adjustments.', '2026-01-12 08:00:00'),
-(26, 12, 3, 'Approved a purchase transaction at the register.', '2026-01-12 17:00:00'),
-(27, 13, 3, 'Updated a pet availability record.', '2026-01-13 02:00:00'),
-(28, 14, 3, 'Completed a grooming appointment handoff.', '2026-01-13 11:00:00'),
-(29, 1, 1, 'Checked a pending adoption record.', '2026-01-13 20:00:00'),
-(30, 2, 2, 'Reviewed a refund request tied to an original sale.', '2026-01-14 05:00:00'),
-(31, 3, 1, 'Answered a customer support conversation.', '2026-01-14 14:00:00'),
-(32, 4, 1, 'Updated a product detail for the catalogue.', '2026-01-14 23:00:00'),
-(33, 5, 1, 'Reviewed store inventory adjustments.', '2026-01-15 08:00:00'),
-(34, 6, 1, 'Approved a purchase transaction at the register.', '2026-01-15 17:00:00'),
-(35, 7, 2, 'Updated a pet availability record.', '2026-01-16 02:00:00'),
-(36, 8, 2, 'Completed a grooming appointment handoff.', '2026-01-16 11:00:00'),
-(37, 9, 2, 'Checked a pending adoption record.', '2026-01-16 20:00:00'),
-(38, 10, 2, 'Reviewed a refund request tied to an original sale.', '2026-01-17 05:00:00'),
-(39, 11, 3, 'Answered a customer support conversation.', '2026-01-17 14:00:00'),
-(40, 12, 3, 'Updated a product detail for the catalogue.', '2026-01-17 23:00:00'),
-(41, 13, 3, 'Reviewed store inventory adjustments.', '2026-01-18 08:00:00'),
-(42, 14, 3, 'Approved a purchase transaction at the register.', '2026-01-18 17:00:00'),
-(43, 1, 1, 'Updated a pet availability record.', '2026-01-19 02:00:00'),
-(44, 2, 2, 'Completed a grooming appointment handoff.', '2026-01-19 11:00:00'),
-(45, 3, 1, 'Checked a pending adoption record.', '2026-01-19 20:00:00'),
-(46, 4, 1, 'Reviewed a refund request tied to an original sale.', '2026-01-20 05:00:00'),
-(47, 5, 1, 'Answered a customer support conversation.', '2026-01-20 14:00:00'),
-(48, 6, 1, 'Updated a product detail for the catalogue.', '2026-01-20 23:00:00'),
-(49, 7, 2, 'Reviewed store inventory adjustments.', '2026-01-21 08:00:00'),
-(50, 8, 2, 'Approved a purchase transaction at the register.', '2026-01-21 17:00:00'),
-(51, 9, 2, 'Updated a pet availability record.', '2026-01-22 02:00:00'),
-(52, 10, 2, 'Completed a grooming appointment handoff.', '2026-01-22 11:00:00'),
-(53, 11, 3, 'Checked a pending adoption record.', '2026-01-22 20:00:00'),
-(54, 12, 3, 'Reviewed a refund request tied to an original sale.', '2026-01-23 05:00:00'),
-(55, 13, 3, 'Answered a customer support conversation.', '2026-01-23 14:00:00'),
-(56, 14, 3, 'Updated a product detail for the catalogue.', '2026-01-23 23:00:00'),
-(57, 1, 1, 'Reviewed store inventory adjustments.', '2026-01-24 08:00:00'),
-(58, 2, 2, 'Approved a purchase transaction at the register.', '2026-01-24 17:00:00'),
-(59, 3, 1, 'Updated a pet availability record.', '2026-01-25 02:00:00'),
-(60, 4, 1, 'Completed a grooming appointment handoff.', '2026-01-25 11:00:00'),
-(61, 5, 1, 'Checked a pending adoption record.', '2026-01-25 20:00:00'),
-(62, 6, 1, 'Reviewed a refund request tied to an original sale.', '2026-01-26 05:00:00'),
-(63, 7, 2, 'Answered a customer support conversation.', '2026-01-26 14:00:00'),
-(64, 8, 2, 'Updated a product detail for the catalogue.', '2026-01-26 23:00:00'),
-(65, 9, 2, 'Reviewed store inventory adjustments.', '2026-01-27 08:00:00'),
-(66, 10, 2, 'Approved a purchase transaction at the register.', '2026-01-27 17:00:00'),
-(67, 11, 3, 'Updated a pet availability record.', '2026-01-28 02:00:00'),
-(68, 12, 3, 'Completed a grooming appointment handoff.', '2026-01-28 11:00:00'),
-(69, 13, 3, 'Checked a pending adoption record.', '2026-01-28 20:00:00'),
-(70, 14, 3, 'Reviewed a refund request tied to an original sale.', '2026-01-29 05:00:00'),
-(71, 1, 1, 'Answered a customer support conversation.', '2026-01-29 14:00:00'),
-(72, 2, 2, 'Updated a product detail for the catalogue.', '2026-01-29 23:00:00'),
-(73, 3, 1, 'Reviewed store inventory adjustments.', '2026-01-30 08:00:00'),
-(74, 4, 1, 'Approved a purchase transaction at the register.', '2026-01-30 17:00:00'),
-(75, 5, 1, 'Updated a pet availability record.', '2026-01-31 02:00:00'),
-(76, 6, 1, 'Completed a grooming appointment handoff.', '2026-01-31 11:00:00'),
-(77, 7, 2, 'Checked a pending adoption record.', '2026-01-31 20:00:00'),
-(78, 8, 2, 'Reviewed a refund request tied to an original sale.', '2026-02-01 05:00:00'),
-(79, 9, 2, 'Answered a customer support conversation.', '2026-02-01 14:00:00'),
-(80, 10, 2, 'Updated a product detail for the catalogue.', '2026-02-01 23:00:00'),
-(81, 11, 3, 'Reviewed store inventory adjustments.', '2026-02-02 08:00:00'),
-(82, 12, 3, 'Approved a purchase transaction at the register.', '2026-02-02 17:00:00'),
-(83, 13, 3, 'Updated a pet availability record.', '2026-02-03 02:00:00'),
-(84, 14, 3, 'Completed a grooming appointment handoff.', '2026-02-03 11:00:00'),
-(85, 1, 1, 'Checked a pending adoption record.', '2026-02-03 20:00:00'),
-(86, 2, 2, 'Reviewed a refund request tied to an original sale.', '2026-02-04 05:00:00'),
-(87, 3, 1, 'Answered a customer support conversation.', '2026-02-04 14:00:00'),
-(88, 4, 1, 'Updated a product detail for the catalogue.', '2026-02-04 23:00:00'),
-(89, 5, 1, 'Reviewed store inventory adjustments.', '2026-02-05 08:00:00'),
-(90, 6, 1, 'Approved a purchase transaction at the register.', '2026-02-05 17:00:00'),
-(91, 7, 2, 'Updated a pet availability record.', '2026-02-06 02:00:00'),
-(92, 8, 2, 'Completed a grooming appointment handoff.', '2026-02-06 11:00:00'),
-(93, 9, 2, 'Checked a pending adoption record.', '2026-02-06 20:00:00'),
-(94, 10, 2, 'Reviewed a refund request tied to an original sale.', '2026-02-07 05:00:00'),
-(95, 11, 3, 'Answered a customer support conversation.', '2026-02-07 14:00:00'),
-(96, 12, 3, 'Updated a product detail for the catalogue.', '2026-02-07 23:00:00'),
-(97, 13, 3, 'Reviewed store inventory adjustments.', '2026-02-08 08:00:00'),
-(98, 14, 3, 'Approved a purchase transaction at the register.', '2026-02-08 17:00:00'),
-(99, 1, 1, 'Updated a pet availability record.', '2026-02-09 02:00:00'),
-(100, 2, 2, 'Completed a grooming appointment handoff.', '2026-02-09 11:00:00'),
-(101, 3, 1, 'Checked a pending adoption record.', '2026-02-09 20:00:00'),
-(102, 4, 1, 'Reviewed a refund request tied to an original sale.', '2026-02-10 05:00:00'),
-(103, 5, 1, 'Answered a customer support conversation.', '2026-02-10 14:00:00'),
-(104, 6, 1, 'Updated a product detail for the catalogue.', '2026-02-10 23:00:00'),
-(105, 7, 2, 'Reviewed store inventory adjustments.', '2026-02-11 08:00:00'),
-(106, 8, 2, 'Approved a purchase transaction at the register.', '2026-02-11 17:00:00'),
-(107, 9, 2, 'Updated a pet availability record.', '2026-02-12 02:00:00'),
-(108, 10, 2, 'Completed a grooming appointment handoff.', '2026-02-12 11:00:00'),
-(109, 11, 3, 'Checked a pending adoption record.', '2026-02-12 20:00:00'),
-(110, 12, 3, 'Reviewed a refund request tied to an original sale.', '2026-02-13 05:00:00'),
-(111, 13, 3, 'Answered a customer support conversation.', '2026-02-13 14:00:00'),
-(112, 14, 3, 'Updated a product detail for the catalogue.', '2026-02-13 23:00:00'),
-(113, 1, 1, 'Reviewed store inventory adjustments.', '2026-02-14 08:00:00'),
-(114, 2, 2, 'Approved a purchase transaction at the register.', '2026-02-14 17:00:00'),
-(115, 3, 1, 'Updated a pet availability record.', '2026-02-15 02:00:00'),
-(116, 4, 1, 'Completed a grooming appointment handoff.', '2026-02-15 11:00:00'),
-(117, 5, 1, 'Checked a pending adoption record.', '2026-02-15 20:00:00'),
-(118, 6, 1, 'Reviewed a refund request tied to an original sale.', '2026-02-16 05:00:00'),
-(119, 7, 2, 'Answered a customer support conversation.', '2026-02-16 14:00:00'),
-(120, 8, 2, 'Updated a product detail for the catalogue.', '2026-02-16 23:00:00');
+INSERT INTO activityLog (logId, userId, storeId, usernameSnapshot, fullNameSnapshot, roleSnapshot, storeNameSnapshot, activity, logTimestamp) VALUES
+(1, 1, 1, 'admin', 'Admin User', 'ADMIN', 'Downtown Branch', 'Reviewed store inventory adjustments.', '2026-01-03 08:00:00'),
+(2, 2, 2, 'morgan.lee', 'Morgan Lee', 'ADMIN', 'North Branch', 'Approved a purchase transaction at the register.', '2026-01-03 17:00:00'),
+(3, 3, 1, 'staff', 'Staff User', 'STAFF', 'Downtown Branch', 'Updated a pet availability record.', '2026-01-04 02:00:00'),
+(4, 4, 1, 'sara.smith', 'Sara Smith', 'STAFF', 'Downtown Branch', 'Completed a grooming appointment handoff.', '2026-01-04 11:00:00'),
+(5, 5, 1, 'david.brown', 'David Brown', 'STAFF', 'Downtown Branch', 'Checked a pending adoption record.', '2026-01-04 20:00:00'),
+(6, 6, 1, 'priya.patel', 'Priya Patel', 'STAFF', 'Downtown Branch', 'Reviewed a refund request tied to an original sale.', '2026-01-05 05:00:00'),
+(7, 7, 2, 'michael.johnson', 'Michael Johnson', 'STAFF', 'North Branch', 'Answered a customer support conversation.', '2026-01-05 14:00:00'),
+(8, 8, 2, 'emma.davis', 'Emma Davis', 'STAFF', 'North Branch', 'Updated a product detail for the catalogue.', '2026-01-05 23:00:00'),
+(9, 9, 2, 'lucas.turner', 'Lucas Turner', 'STAFF', 'North Branch', 'Reviewed store inventory adjustments.', '2026-01-06 08:00:00'),
+(10, 10, 2, 'nina.green', 'Nina Green', 'STAFF', 'North Branch', 'Approved a purchase transaction at the register.', '2026-01-06 17:00:00'),
+(11, 11, 3, 'lisa.williams', 'Lisa Williams', 'STAFF', 'West Side Store', 'Updated a pet availability record.', '2026-01-07 02:00:00'),
+(12, 12, 3, 'daniel.moore', 'Daniel Moore', 'STAFF', 'West Side Store', 'Completed a grooming appointment handoff.', '2026-01-07 11:00:00'),
+(13, 13, 3, 'chloe.martin', 'Chloe Martin', 'STAFF', 'West Side Store', 'Checked a pending adoption record.', '2026-01-07 20:00:00'),
+(14, 14, 3, 'owen.baker', 'Owen Baker', 'STAFF', 'West Side Store', 'Reviewed a refund request tied to an original sale.', '2026-01-08 05:00:00'),
+(15, 1, 1, 'admin', 'Admin User', 'ADMIN', 'Downtown Branch', 'Answered a customer support conversation.', '2026-01-08 14:00:00'),
+(16, 2, 2, 'morgan.lee', 'Morgan Lee', 'ADMIN', 'North Branch', 'Updated a product detail for the catalogue.', '2026-01-08 23:00:00'),
+(17, 3, 1, 'staff', 'Staff User', 'STAFF', 'Downtown Branch', 'Reviewed store inventory adjustments.', '2026-01-09 08:00:00'),
+(18, 4, 1, 'sara.smith', 'Sara Smith', 'STAFF', 'Downtown Branch', 'Approved a purchase transaction at the register.', '2026-01-09 17:00:00'),
+(19, 5, 1, 'david.brown', 'David Brown', 'STAFF', 'Downtown Branch', 'Updated a pet availability record.', '2026-01-10 02:00:00'),
+(20, 6, 1, 'priya.patel', 'Priya Patel', 'STAFF', 'Downtown Branch', 'Completed a grooming appointment handoff.', '2026-01-10 11:00:00'),
+(21, 7, 2, 'michael.johnson', 'Michael Johnson', 'STAFF', 'North Branch', 'Checked a pending adoption record.', '2026-01-10 20:00:00'),
+(22, 8, 2, 'emma.davis', 'Emma Davis', 'STAFF', 'North Branch', 'Reviewed a refund request tied to an original sale.', '2026-01-11 05:00:00'),
+(23, 9, 2, 'lucas.turner', 'Lucas Turner', 'STAFF', 'North Branch', 'Answered a customer support conversation.', '2026-01-11 14:00:00'),
+(24, 10, 2, 'nina.green', 'Nina Green', 'STAFF', 'North Branch', 'Updated a product detail for the catalogue.', '2026-01-11 23:00:00'),
+(25, 11, 3, 'lisa.williams', 'Lisa Williams', 'STAFF', 'West Side Store', 'Reviewed store inventory adjustments.', '2026-01-12 08:00:00'),
+(26, 12, 3, 'daniel.moore', 'Daniel Moore', 'STAFF', 'West Side Store', 'Approved a purchase transaction at the register.', '2026-01-12 17:00:00'),
+(27, 13, 3, 'chloe.martin', 'Chloe Martin', 'STAFF', 'West Side Store', 'Updated a pet availability record.', '2026-01-13 02:00:00'),
+(28, 14, 3, 'owen.baker', 'Owen Baker', 'STAFF', 'West Side Store', 'Completed a grooming appointment handoff.', '2026-01-13 11:00:00'),
+(29, 1, 1, 'admin', 'Admin User', 'ADMIN', 'Downtown Branch', 'Checked a pending adoption record.', '2026-01-13 20:00:00'),
+(30, 2, 2, 'morgan.lee', 'Morgan Lee', 'ADMIN', 'North Branch', 'Reviewed a refund request tied to an original sale.', '2026-01-14 05:00:00'),
+(31, 3, 1, 'staff', 'Staff User', 'STAFF', 'Downtown Branch', 'Answered a customer support conversation.', '2026-01-14 14:00:00'),
+(32, 4, 1, 'sara.smith', 'Sara Smith', 'STAFF', 'Downtown Branch', 'Updated a product detail for the catalogue.', '2026-01-14 23:00:00'),
+(33, 5, 1, 'david.brown', 'David Brown', 'STAFF', 'Downtown Branch', 'Reviewed store inventory adjustments.', '2026-01-15 08:00:00'),
+(34, 6, 1, 'priya.patel', 'Priya Patel', 'STAFF', 'Downtown Branch', 'Approved a purchase transaction at the register.', '2026-01-15 17:00:00'),
+(35, 7, 2, 'michael.johnson', 'Michael Johnson', 'STAFF', 'North Branch', 'Updated a pet availability record.', '2026-01-16 02:00:00'),
+(36, 8, 2, 'emma.davis', 'Emma Davis', 'STAFF', 'North Branch', 'Completed a grooming appointment handoff.', '2026-01-16 11:00:00'),
+(37, 9, 2, 'lucas.turner', 'Lucas Turner', 'STAFF', 'North Branch', 'Checked a pending adoption record.', '2026-01-16 20:00:00'),
+(38, 10, 2, 'nina.green', 'Nina Green', 'STAFF', 'North Branch', 'Reviewed a refund request tied to an original sale.', '2026-01-17 05:00:00'),
+(39, 11, 3, 'lisa.williams', 'Lisa Williams', 'STAFF', 'West Side Store', 'Answered a customer support conversation.', '2026-01-17 14:00:00'),
+(40, 12, 3, 'daniel.moore', 'Daniel Moore', 'STAFF', 'West Side Store', 'Updated a product detail for the catalogue.', '2026-01-17 23:00:00'),
+(41, 13, 3, 'chloe.martin', 'Chloe Martin', 'STAFF', 'West Side Store', 'Reviewed store inventory adjustments.', '2026-01-18 08:00:00'),
+(42, 14, 3, 'owen.baker', 'Owen Baker', 'STAFF', 'West Side Store', 'Approved a purchase transaction at the register.', '2026-01-18 17:00:00'),
+(43, 1, 1, 'admin', 'Admin User', 'ADMIN', 'Downtown Branch', 'Updated a pet availability record.', '2026-01-19 02:00:00'),
+(44, 2, 2, 'morgan.lee', 'Morgan Lee', 'ADMIN', 'North Branch', 'Completed a grooming appointment handoff.', '2026-01-19 11:00:00'),
+(45, 3, 1, 'staff', 'Staff User', 'STAFF', 'Downtown Branch', 'Checked a pending adoption record.', '2026-01-19 20:00:00'),
+(46, 4, 1, 'sara.smith', 'Sara Smith', 'STAFF', 'Downtown Branch', 'Reviewed a refund request tied to an original sale.', '2026-01-20 05:00:00'),
+(47, 5, 1, 'david.brown', 'David Brown', 'STAFF', 'Downtown Branch', 'Answered a customer support conversation.', '2026-01-20 14:00:00'),
+(48, 6, 1, 'priya.patel', 'Priya Patel', 'STAFF', 'Downtown Branch', 'Updated a product detail for the catalogue.', '2026-01-20 23:00:00'),
+(49, 7, 2, 'michael.johnson', 'Michael Johnson', 'STAFF', 'North Branch', 'Reviewed store inventory adjustments.', '2026-01-21 08:00:00'),
+(50, 8, 2, 'emma.davis', 'Emma Davis', 'STAFF', 'North Branch', 'Approved a purchase transaction at the register.', '2026-01-21 17:00:00'),
+(51, 9, 2, 'lucas.turner', 'Lucas Turner', 'STAFF', 'North Branch', 'Updated a pet availability record.', '2026-01-22 02:00:00'),
+(52, 10, 2, 'nina.green', 'Nina Green', 'STAFF', 'North Branch', 'Completed a grooming appointment handoff.', '2026-01-22 11:00:00'),
+(53, 11, 3, 'lisa.williams', 'Lisa Williams', 'STAFF', 'West Side Store', 'Checked a pending adoption record.', '2026-01-22 20:00:00'),
+(54, 12, 3, 'daniel.moore', 'Daniel Moore', 'STAFF', 'West Side Store', 'Reviewed a refund request tied to an original sale.', '2026-01-23 05:00:00'),
+(55, 13, 3, 'chloe.martin', 'Chloe Martin', 'STAFF', 'West Side Store', 'Answered a customer support conversation.', '2026-01-23 14:00:00'),
+(56, 14, 3, 'owen.baker', 'Owen Baker', 'STAFF', 'West Side Store', 'Updated a product detail for the catalogue.', '2026-01-23 23:00:00'),
+(57, 1, 1, 'admin', 'Admin User', 'ADMIN', 'Downtown Branch', 'Reviewed store inventory adjustments.', '2026-01-24 08:00:00'),
+(58, 2, 2, 'morgan.lee', 'Morgan Lee', 'ADMIN', 'North Branch', 'Approved a purchase transaction at the register.', '2026-01-24 17:00:00'),
+(59, 3, 1, 'staff', 'Staff User', 'STAFF', 'Downtown Branch', 'Updated a pet availability record.', '2026-01-25 02:00:00'),
+(60, 4, 1, 'sara.smith', 'Sara Smith', 'STAFF', 'Downtown Branch', 'Completed a grooming appointment handoff.', '2026-01-25 11:00:00'),
+(61, 5, 1, 'david.brown', 'David Brown', 'STAFF', 'Downtown Branch', 'Checked a pending adoption record.', '2026-01-25 20:00:00'),
+(62, 6, 1, 'priya.patel', 'Priya Patel', 'STAFF', 'Downtown Branch', 'Reviewed a refund request tied to an original sale.', '2026-01-26 05:00:00'),
+(63, 7, 2, 'michael.johnson', 'Michael Johnson', 'STAFF', 'North Branch', 'Answered a customer support conversation.', '2026-01-26 14:00:00'),
+(64, 8, 2, 'emma.davis', 'Emma Davis', 'STAFF', 'North Branch', 'Updated a product detail for the catalogue.', '2026-01-26 23:00:00'),
+(65, 9, 2, 'lucas.turner', 'Lucas Turner', 'STAFF', 'North Branch', 'Reviewed store inventory adjustments.', '2026-01-27 08:00:00'),
+(66, 10, 2, 'nina.green', 'Nina Green', 'STAFF', 'North Branch', 'Approved a purchase transaction at the register.', '2026-01-27 17:00:00'),
+(67, 11, 3, 'lisa.williams', 'Lisa Williams', 'STAFF', 'West Side Store', 'Updated a pet availability record.', '2026-01-28 02:00:00'),
+(68, 12, 3, 'daniel.moore', 'Daniel Moore', 'STAFF', 'West Side Store', 'Completed a grooming appointment handoff.', '2026-01-28 11:00:00'),
+(69, 13, 3, 'chloe.martin', 'Chloe Martin', 'STAFF', 'West Side Store', 'Checked a pending adoption record.', '2026-01-28 20:00:00'),
+(70, 14, 3, 'owen.baker', 'Owen Baker', 'STAFF', 'West Side Store', 'Reviewed a refund request tied to an original sale.', '2026-01-29 05:00:00'),
+(71, 1, 1, 'admin', 'Admin User', 'ADMIN', 'Downtown Branch', 'Answered a customer support conversation.', '2026-01-29 14:00:00'),
+(72, 2, 2, 'morgan.lee', 'Morgan Lee', 'ADMIN', 'North Branch', 'Updated a product detail for the catalogue.', '2026-01-29 23:00:00'),
+(73, 3, 1, 'staff', 'Staff User', 'STAFF', 'Downtown Branch', 'Reviewed store inventory adjustments.', '2026-01-30 08:00:00'),
+(74, 4, 1, 'sara.smith', 'Sara Smith', 'STAFF', 'Downtown Branch', 'Approved a purchase transaction at the register.', '2026-01-30 17:00:00'),
+(75, 5, 1, 'david.brown', 'David Brown', 'STAFF', 'Downtown Branch', 'Updated a pet availability record.', '2026-01-31 02:00:00'),
+(76, 6, 1, 'priya.patel', 'Priya Patel', 'STAFF', 'Downtown Branch', 'Completed a grooming appointment handoff.', '2026-01-31 11:00:00'),
+(77, 7, 2, 'michael.johnson', 'Michael Johnson', 'STAFF', 'North Branch', 'Checked a pending adoption record.', '2026-01-31 20:00:00'),
+(78, 8, 2, 'emma.davis', 'Emma Davis', 'STAFF', 'North Branch', 'Reviewed a refund request tied to an original sale.', '2026-02-01 05:00:00'),
+(79, 9, 2, 'lucas.turner', 'Lucas Turner', 'STAFF', 'North Branch', 'Answered a customer support conversation.', '2026-02-01 14:00:00'),
+(80, 10, 2, 'nina.green', 'Nina Green', 'STAFF', 'North Branch', 'Updated a product detail for the catalogue.', '2026-02-01 23:00:00'),
+(81, 11, 3, 'lisa.williams', 'Lisa Williams', 'STAFF', 'West Side Store', 'Reviewed store inventory adjustments.', '2026-02-02 08:00:00'),
+(82, 12, 3, 'daniel.moore', 'Daniel Moore', 'STAFF', 'West Side Store', 'Approved a purchase transaction at the register.', '2026-02-02 17:00:00'),
+(83, 13, 3, 'chloe.martin', 'Chloe Martin', 'STAFF', 'West Side Store', 'Updated a pet availability record.', '2026-02-03 02:00:00'),
+(84, 14, 3, 'owen.baker', 'Owen Baker', 'STAFF', 'West Side Store', 'Completed a grooming appointment handoff.', '2026-02-03 11:00:00'),
+(85, 1, 1, 'admin', 'Admin User', 'ADMIN', 'Downtown Branch', 'Checked a pending adoption record.', '2026-02-03 20:00:00'),
+(86, 2, 2, 'morgan.lee', 'Morgan Lee', 'ADMIN', 'North Branch', 'Reviewed a refund request tied to an original sale.', '2026-02-04 05:00:00'),
+(87, 3, 1, 'staff', 'Staff User', 'STAFF', 'Downtown Branch', 'Answered a customer support conversation.', '2026-02-04 14:00:00'),
+(88, 4, 1, 'sara.smith', 'Sara Smith', 'STAFF', 'Downtown Branch', 'Updated a product detail for the catalogue.', '2026-02-04 23:00:00'),
+(89, 5, 1, 'david.brown', 'David Brown', 'STAFF', 'Downtown Branch', 'Reviewed store inventory adjustments.', '2026-02-05 08:00:00'),
+(90, 6, 1, 'priya.patel', 'Priya Patel', 'STAFF', 'Downtown Branch', 'Approved a purchase transaction at the register.', '2026-02-05 17:00:00'),
+(91, 7, 2, 'michael.johnson', 'Michael Johnson', 'STAFF', 'North Branch', 'Updated a pet availability record.', '2026-02-06 02:00:00'),
+(92, 8, 2, 'emma.davis', 'Emma Davis', 'STAFF', 'North Branch', 'Completed a grooming appointment handoff.', '2026-02-06 11:00:00'),
+(93, 9, 2, 'lucas.turner', 'Lucas Turner', 'STAFF', 'North Branch', 'Checked a pending adoption record.', '2026-02-06 20:00:00'),
+(94, 10, 2, 'nina.green', 'Nina Green', 'STAFF', 'North Branch', 'Reviewed a refund request tied to an original sale.', '2026-02-07 05:00:00'),
+(95, 11, 3, 'lisa.williams', 'Lisa Williams', 'STAFF', 'West Side Store', 'Answered a customer support conversation.', '2026-02-07 14:00:00'),
+(96, 12, 3, 'daniel.moore', 'Daniel Moore', 'STAFF', 'West Side Store', 'Updated a product detail for the catalogue.', '2026-02-07 23:00:00'),
+(97, 13, 3, 'chloe.martin', 'Chloe Martin', 'STAFF', 'West Side Store', 'Reviewed store inventory adjustments.', '2026-02-08 08:00:00'),
+(98, 14, 3, 'owen.baker', 'Owen Baker', 'STAFF', 'West Side Store', 'Approved a purchase transaction at the register.', '2026-02-08 17:00:00'),
+(99, 1, 1, 'admin', 'Admin User', 'ADMIN', 'Downtown Branch', 'Updated a pet availability record.', '2026-02-09 02:00:00'),
+(100, 2, 2, 'morgan.lee', 'Morgan Lee', 'ADMIN', 'North Branch', 'Completed a grooming appointment handoff.', '2026-02-09 11:00:00'),
+(101, 3, 1, 'staff', 'Staff User', 'STAFF', 'Downtown Branch', 'Checked a pending adoption record.', '2026-02-09 20:00:00'),
+(102, 4, 1, 'sara.smith', 'Sara Smith', 'STAFF', 'Downtown Branch', 'Reviewed a refund request tied to an original sale.', '2026-02-10 05:00:00'),
+(103, 5, 1, 'david.brown', 'David Brown', 'STAFF', 'Downtown Branch', 'Answered a customer support conversation.', '2026-02-10 14:00:00'),
+(104, 6, 1, 'priya.patel', 'Priya Patel', 'STAFF', 'Downtown Branch', 'Updated a product detail for the catalogue.', '2026-02-10 23:00:00'),
+(105, 7, 2, 'michael.johnson', 'Michael Johnson', 'STAFF', 'North Branch', 'Reviewed store inventory adjustments.', '2026-02-11 08:00:00'),
+(106, 8, 2, 'emma.davis', 'Emma Davis', 'STAFF', 'North Branch', 'Approved a purchase transaction at the register.', '2026-02-11 17:00:00'),
+(107, 9, 2, 'lucas.turner', 'Lucas Turner', 'STAFF', 'North Branch', 'Updated a pet availability record.', '2026-02-12 02:00:00'),
+(108, 10, 2, 'nina.green', 'Nina Green', 'STAFF', 'North Branch', 'Completed a grooming appointment handoff.', '2026-02-12 11:00:00'),
+(109, 11, 3, 'lisa.williams', 'Lisa Williams', 'STAFF', 'West Side Store', 'Checked a pending adoption record.', '2026-02-12 20:00:00'),
+(110, 12, 3, 'daniel.moore', 'Daniel Moore', 'STAFF', 'West Side Store', 'Reviewed a refund request tied to an original sale.', '2026-02-13 05:00:00'),
+(111, 13, 3, 'chloe.martin', 'Chloe Martin', 'STAFF', 'West Side Store', 'Answered a customer support conversation.', '2026-02-13 14:00:00'),
+(112, 14, 3, 'owen.baker', 'Owen Baker', 'STAFF', 'West Side Store', 'Updated a product detail for the catalogue.', '2026-02-13 23:00:00'),
+(113, 1, 1, 'admin', 'Admin User', 'ADMIN', 'Downtown Branch', 'Reviewed store inventory adjustments.', '2026-02-14 08:00:00'),
+(114, 2, 2, 'morgan.lee', 'Morgan Lee', 'ADMIN', 'North Branch', 'Approved a purchase transaction at the register.', '2026-02-14 17:00:00'),
+(115, 3, 1, 'staff', 'Staff User', 'STAFF', 'Downtown Branch', 'Updated a pet availability record.', '2026-02-15 02:00:00'),
+(116, 4, 1, 'sara.smith', 'Sara Smith', 'STAFF', 'Downtown Branch', 'Completed a grooming appointment handoff.', '2026-02-15 11:00:00'),
+(117, 5, 1, 'david.brown', 'David Brown', 'STAFF', 'Downtown Branch', 'Checked a pending adoption record.', '2026-02-15 20:00:00'),
+(118, 6, 1, 'priya.patel', 'Priya Patel', 'STAFF', 'Downtown Branch', 'Reviewed a refund request tied to an original sale.', '2026-02-16 05:00:00'),
+(119, 7, 2, 'michael.johnson', 'Michael Johnson', 'STAFF', 'North Branch', 'Answered a customer support conversation.', '2026-02-16 14:00:00'),
+(120, 8, 2, 'emma.davis', 'Emma Davis', 'STAFF', 'North Branch', 'Updated a product detail for the catalogue.', '2026-02-16 23:00:00');
\ No newline at end of file
diff --git a/backend/uploads/avatars/001.webp b/backend/uploads/avatars/001.webp
new file mode 100644
index 00000000..c7f1714f
Binary files /dev/null and b/backend/uploads/avatars/001.webp differ
diff --git a/backend/uploads/avatars/002.webp b/backend/uploads/avatars/002.webp
new file mode 100644
index 00000000..0415e705
Binary files /dev/null and b/backend/uploads/avatars/002.webp differ
diff --git a/backend/uploads/avatars/003.webp b/backend/uploads/avatars/003.webp
new file mode 100644
index 00000000..999af5e0
Binary files /dev/null and b/backend/uploads/avatars/003.webp differ
diff --git a/backend/uploads/avatars/004.webp b/backend/uploads/avatars/004.webp
new file mode 100644
index 00000000..e739f364
Binary files /dev/null and b/backend/uploads/avatars/004.webp differ
diff --git a/backend/uploads/avatars/005.webp b/backend/uploads/avatars/005.webp
new file mode 100644
index 00000000..cf53b9cc
Binary files /dev/null and b/backend/uploads/avatars/005.webp differ
diff --git a/backend/uploads/avatars/006.webp b/backend/uploads/avatars/006.webp
new file mode 100644
index 00000000..9bbfb611
Binary files /dev/null and b/backend/uploads/avatars/006.webp differ
diff --git a/backend/uploads/avatars/007.webp b/backend/uploads/avatars/007.webp
new file mode 100644
index 00000000..43fe6076
Binary files /dev/null and b/backend/uploads/avatars/007.webp differ
diff --git a/backend/uploads/avatars/008.webp b/backend/uploads/avatars/008.webp
new file mode 100644
index 00000000..b47718dd
Binary files /dev/null and b/backend/uploads/avatars/008.webp differ
diff --git a/backend/uploads/avatars/009.webp b/backend/uploads/avatars/009.webp
new file mode 100644
index 00000000..cda172e5
Binary files /dev/null and b/backend/uploads/avatars/009.webp differ
diff --git a/backend/uploads/avatars/010.webp b/backend/uploads/avatars/010.webp
new file mode 100644
index 00000000..8bb1f2b4
Binary files /dev/null and b/backend/uploads/avatars/010.webp differ
diff --git a/backend/uploads/avatars/011.webp b/backend/uploads/avatars/011.webp
new file mode 100644
index 00000000..8050e761
Binary files /dev/null and b/backend/uploads/avatars/011.webp differ
diff --git a/backend/uploads/avatars/012.webp b/backend/uploads/avatars/012.webp
new file mode 100644
index 00000000..3f7972fb
Binary files /dev/null and b/backend/uploads/avatars/012.webp differ
diff --git a/backend/uploads/avatars/013.webp b/backend/uploads/avatars/013.webp
new file mode 100644
index 00000000..ae652e3e
Binary files /dev/null and b/backend/uploads/avatars/013.webp differ
diff --git a/backend/uploads/avatars/014.webp b/backend/uploads/avatars/014.webp
new file mode 100644
index 00000000..bb6f3cc8
Binary files /dev/null and b/backend/uploads/avatars/014.webp differ
diff --git a/backend/uploads/avatars/015.webp b/backend/uploads/avatars/015.webp
new file mode 100644
index 00000000..7a89ff39
Binary files /dev/null and b/backend/uploads/avatars/015.webp differ
diff --git a/backend/uploads/avatars/016.webp b/backend/uploads/avatars/016.webp
new file mode 100644
index 00000000..7b3c61ba
Binary files /dev/null and b/backend/uploads/avatars/016.webp differ
diff --git a/backend/uploads/avatars/017.webp b/backend/uploads/avatars/017.webp
new file mode 100644
index 00000000..dd561abf
Binary files /dev/null and b/backend/uploads/avatars/017.webp differ
diff --git a/backend/uploads/avatars/018.webp b/backend/uploads/avatars/018.webp
new file mode 100644
index 00000000..ab48bf12
Binary files /dev/null and b/backend/uploads/avatars/018.webp differ
diff --git a/backend/uploads/avatars/019.webp b/backend/uploads/avatars/019.webp
new file mode 100644
index 00000000..052ba76a
Binary files /dev/null and b/backend/uploads/avatars/019.webp differ
diff --git a/backend/uploads/avatars/020.webp b/backend/uploads/avatars/020.webp
new file mode 100644
index 00000000..0399188a
Binary files /dev/null and b/backend/uploads/avatars/020.webp differ
diff --git a/backend/uploads/avatars/021.webp b/backend/uploads/avatars/021.webp
new file mode 100644
index 00000000..eb6409fe
Binary files /dev/null and b/backend/uploads/avatars/021.webp differ
diff --git a/backend/uploads/avatars/022.webp b/backend/uploads/avatars/022.webp
new file mode 100644
index 00000000..a8823f17
Binary files /dev/null and b/backend/uploads/avatars/022.webp differ
diff --git a/backend/uploads/avatars/023.webp b/backend/uploads/avatars/023.webp
new file mode 100644
index 00000000..284214c0
Binary files /dev/null and b/backend/uploads/avatars/023.webp differ
diff --git a/backend/uploads/avatars/024.webp b/backend/uploads/avatars/024.webp
new file mode 100644
index 00000000..8c9ad2d0
Binary files /dev/null and b/backend/uploads/avatars/024.webp differ
diff --git a/backend/uploads/avatars/025.webp b/backend/uploads/avatars/025.webp
new file mode 100644
index 00000000..2f196a7e
Binary files /dev/null and b/backend/uploads/avatars/025.webp differ
diff --git a/backend/uploads/avatars/026.webp b/backend/uploads/avatars/026.webp
new file mode 100644
index 00000000..a33cdaf9
Binary files /dev/null and b/backend/uploads/avatars/026.webp differ
diff --git a/backend/uploads/avatars/027.webp b/backend/uploads/avatars/027.webp
new file mode 100644
index 00000000..e08c3563
Binary files /dev/null and b/backend/uploads/avatars/027.webp differ
diff --git a/backend/uploads/avatars/028.webp b/backend/uploads/avatars/028.webp
new file mode 100644
index 00000000..7b47cb25
Binary files /dev/null and b/backend/uploads/avatars/028.webp differ
diff --git a/backend/uploads/avatars/029.webp b/backend/uploads/avatars/029.webp
new file mode 100644
index 00000000..f7a68ee4
Binary files /dev/null and b/backend/uploads/avatars/029.webp differ
diff --git a/backend/uploads/avatars/030.webp b/backend/uploads/avatars/030.webp
new file mode 100644
index 00000000..fbbe64c2
Binary files /dev/null and b/backend/uploads/avatars/030.webp differ
diff --git a/backend/uploads/avatars/031.webp b/backend/uploads/avatars/031.webp
new file mode 100644
index 00000000..8111ca66
Binary files /dev/null and b/backend/uploads/avatars/031.webp differ
diff --git a/backend/uploads/avatars/032.webp b/backend/uploads/avatars/032.webp
new file mode 100644
index 00000000..f2e3826b
Binary files /dev/null and b/backend/uploads/avatars/032.webp differ
diff --git a/backend/uploads/avatars/033.webp b/backend/uploads/avatars/033.webp
new file mode 100644
index 00000000..a38e4d53
Binary files /dev/null and b/backend/uploads/avatars/033.webp differ
diff --git a/backend/uploads/avatars/034.webp b/backend/uploads/avatars/034.webp
new file mode 100644
index 00000000..4e69c5f5
Binary files /dev/null and b/backend/uploads/avatars/034.webp differ
diff --git a/backend/uploads/avatars/035.webp b/backend/uploads/avatars/035.webp
new file mode 100644
index 00000000..fd75c7bc
Binary files /dev/null and b/backend/uploads/avatars/035.webp differ
diff --git a/backend/uploads/avatars/036.webp b/backend/uploads/avatars/036.webp
new file mode 100644
index 00000000..9e07d612
Binary files /dev/null and b/backend/uploads/avatars/036.webp differ
diff --git a/backend/uploads/avatars/037.webp b/backend/uploads/avatars/037.webp
new file mode 100644
index 00000000..003ce914
Binary files /dev/null and b/backend/uploads/avatars/037.webp differ
diff --git a/backend/uploads/avatars/038.webp b/backend/uploads/avatars/038.webp
new file mode 100644
index 00000000..b50c35d4
Binary files /dev/null and b/backend/uploads/avatars/038.webp differ
diff --git a/backend/uploads/avatars/039.webp b/backend/uploads/avatars/039.webp
new file mode 100644
index 00000000..62f94011
Binary files /dev/null and b/backend/uploads/avatars/039.webp differ
diff --git a/backend/uploads/avatars/040.webp b/backend/uploads/avatars/040.webp
new file mode 100644
index 00000000..db5e6d97
Binary files /dev/null and b/backend/uploads/avatars/040.webp differ
diff --git a/backend/uploads/avatars/041.webp b/backend/uploads/avatars/041.webp
new file mode 100644
index 00000000..03be9480
Binary files /dev/null and b/backend/uploads/avatars/041.webp differ
diff --git a/backend/uploads/avatars/042.webp b/backend/uploads/avatars/042.webp
new file mode 100644
index 00000000..eb6ecaed
Binary files /dev/null and b/backend/uploads/avatars/042.webp differ
diff --git a/backend/uploads/avatars/043.webp b/backend/uploads/avatars/043.webp
new file mode 100644
index 00000000..3aa7677f
Binary files /dev/null and b/backend/uploads/avatars/043.webp differ
diff --git a/backend/uploads/avatars/044.webp b/backend/uploads/avatars/044.webp
new file mode 100644
index 00000000..619bbabc
Binary files /dev/null and b/backend/uploads/avatars/044.webp differ
diff --git a/backend/uploads/avatars/045.webp b/backend/uploads/avatars/045.webp
new file mode 100644
index 00000000..0a2519fb
Binary files /dev/null and b/backend/uploads/avatars/045.webp differ
diff --git a/backend/uploads/avatars/046.webp b/backend/uploads/avatars/046.webp
new file mode 100644
index 00000000..f30b5a8c
Binary files /dev/null and b/backend/uploads/avatars/046.webp differ
diff --git a/backend/uploads/avatars/047.webp b/backend/uploads/avatars/047.webp
new file mode 100644
index 00000000..854d8498
Binary files /dev/null and b/backend/uploads/avatars/047.webp differ
diff --git a/backend/uploads/avatars/048.webp b/backend/uploads/avatars/048.webp
new file mode 100644
index 00000000..f25181b8
Binary files /dev/null and b/backend/uploads/avatars/048.webp differ
diff --git a/backend/uploads/avatars/049.webp b/backend/uploads/avatars/049.webp
new file mode 100644
index 00000000..9d51aa1f
Binary files /dev/null and b/backend/uploads/avatars/049.webp differ
diff --git a/backend/uploads/avatars/050.webp b/backend/uploads/avatars/050.webp
new file mode 100644
index 00000000..6b8124a5
Binary files /dev/null and b/backend/uploads/avatars/050.webp differ
diff --git a/backend/uploads/avatars/051.webp b/backend/uploads/avatars/051.webp
new file mode 100644
index 00000000..863026d4
Binary files /dev/null and b/backend/uploads/avatars/051.webp differ
diff --git a/backend/uploads/avatars/052.webp b/backend/uploads/avatars/052.webp
new file mode 100644
index 00000000..81b8367f
Binary files /dev/null and b/backend/uploads/avatars/052.webp differ
diff --git a/backend/uploads/avatars/053.webp b/backend/uploads/avatars/053.webp
new file mode 100644
index 00000000..6f5c9e8d
Binary files /dev/null and b/backend/uploads/avatars/053.webp differ
diff --git a/backend/uploads/avatars/054.webp b/backend/uploads/avatars/054.webp
new file mode 100644
index 00000000..0bf4afdb
Binary files /dev/null and b/backend/uploads/avatars/054.webp differ
diff --git a/backend/uploads/avatars/055.webp b/backend/uploads/avatars/055.webp
new file mode 100644
index 00000000..46ca8b02
Binary files /dev/null and b/backend/uploads/avatars/055.webp differ
diff --git a/backend/uploads/avatars/056.webp b/backend/uploads/avatars/056.webp
new file mode 100644
index 00000000..a39c8017
Binary files /dev/null and b/backend/uploads/avatars/056.webp differ
diff --git a/backend/uploads/avatars/057.webp b/backend/uploads/avatars/057.webp
new file mode 100644
index 00000000..d49167d1
Binary files /dev/null and b/backend/uploads/avatars/057.webp differ
diff --git a/backend/uploads/avatars/058.webp b/backend/uploads/avatars/058.webp
new file mode 100644
index 00000000..676981ab
Binary files /dev/null and b/backend/uploads/avatars/058.webp differ
diff --git a/backend/uploads/avatars/059.webp b/backend/uploads/avatars/059.webp
new file mode 100644
index 00000000..19a44217
Binary files /dev/null and b/backend/uploads/avatars/059.webp differ
diff --git a/backend/uploads/avatars/060.webp b/backend/uploads/avatars/060.webp
new file mode 100644
index 00000000..bfbff000
Binary files /dev/null and b/backend/uploads/avatars/060.webp differ
diff --git a/backend/uploads/avatars/061.webp b/backend/uploads/avatars/061.webp
new file mode 100644
index 00000000..2444ea30
Binary files /dev/null and b/backend/uploads/avatars/061.webp differ
diff --git a/backend/uploads/avatars/062.webp b/backend/uploads/avatars/062.webp
new file mode 100644
index 00000000..b88275d1
Binary files /dev/null and b/backend/uploads/avatars/062.webp differ
diff --git a/backend/uploads/avatars/063.webp b/backend/uploads/avatars/063.webp
new file mode 100644
index 00000000..2d177a41
Binary files /dev/null and b/backend/uploads/avatars/063.webp differ
diff --git a/backend/uploads/avatars/064.webp b/backend/uploads/avatars/064.webp
new file mode 100644
index 00000000..7d3e350c
Binary files /dev/null and b/backend/uploads/avatars/064.webp differ
diff --git a/backend/uploads/avatars/065.webp b/backend/uploads/avatars/065.webp
new file mode 100644
index 00000000..aa1e955c
Binary files /dev/null and b/backend/uploads/avatars/065.webp differ
diff --git a/backend/uploads/avatars/066.webp b/backend/uploads/avatars/066.webp
new file mode 100644
index 00000000..ae4eea9f
Binary files /dev/null and b/backend/uploads/avatars/066.webp differ
diff --git a/backend/uploads/avatars/067.webp b/backend/uploads/avatars/067.webp
new file mode 100644
index 00000000..22419497
Binary files /dev/null and b/backend/uploads/avatars/067.webp differ
diff --git a/backend/uploads/avatars/068.webp b/backend/uploads/avatars/068.webp
new file mode 100644
index 00000000..60b6193a
Binary files /dev/null and b/backend/uploads/avatars/068.webp differ
diff --git a/backend/uploads/avatars/069.webp b/backend/uploads/avatars/069.webp
new file mode 100644
index 00000000..8930f5ff
Binary files /dev/null and b/backend/uploads/avatars/069.webp differ
diff --git a/backend/uploads/avatars/070.webp b/backend/uploads/avatars/070.webp
new file mode 100644
index 00000000..28e0089b
Binary files /dev/null and b/backend/uploads/avatars/070.webp differ
diff --git a/backend/uploads/avatars/071.webp b/backend/uploads/avatars/071.webp
new file mode 100644
index 00000000..231c3ba7
Binary files /dev/null and b/backend/uploads/avatars/071.webp differ
diff --git a/backend/uploads/avatars/072.webp b/backend/uploads/avatars/072.webp
new file mode 100644
index 00000000..c948d59c
Binary files /dev/null and b/backend/uploads/avatars/072.webp differ
diff --git a/backend/uploads/avatars/073.webp b/backend/uploads/avatars/073.webp
new file mode 100644
index 00000000..f2f213c1
Binary files /dev/null and b/backend/uploads/avatars/073.webp differ
diff --git a/backend/uploads/avatars/074.webp b/backend/uploads/avatars/074.webp
new file mode 100644
index 00000000..4948d52a
Binary files /dev/null and b/backend/uploads/avatars/074.webp differ
diff --git a/backend/uploads/avatars/075.webp b/backend/uploads/avatars/075.webp
new file mode 100644
index 00000000..3262c091
Binary files /dev/null and b/backend/uploads/avatars/075.webp differ
diff --git a/backend/uploads/avatars/076.webp b/backend/uploads/avatars/076.webp
new file mode 100644
index 00000000..110d6971
Binary files /dev/null and b/backend/uploads/avatars/076.webp differ
diff --git a/backend/uploads/avatars/077.webp b/backend/uploads/avatars/077.webp
new file mode 100644
index 00000000..c19fa09a
Binary files /dev/null and b/backend/uploads/avatars/077.webp differ
diff --git a/backend/uploads/avatars/078.webp b/backend/uploads/avatars/078.webp
new file mode 100644
index 00000000..849e4339
Binary files /dev/null and b/backend/uploads/avatars/078.webp differ
diff --git a/backend/uploads/avatars/079.webp b/backend/uploads/avatars/079.webp
new file mode 100644
index 00000000..11385a2b
Binary files /dev/null and b/backend/uploads/avatars/079.webp differ
diff --git a/backend/uploads/avatars/080.webp b/backend/uploads/avatars/080.webp
new file mode 100644
index 00000000..1a3299ff
Binary files /dev/null and b/backend/uploads/avatars/080.webp differ
diff --git a/backend/uploads/avatars/081.webp b/backend/uploads/avatars/081.webp
new file mode 100644
index 00000000..5d5066e4
Binary files /dev/null and b/backend/uploads/avatars/081.webp differ
diff --git a/backend/uploads/avatars/082.webp b/backend/uploads/avatars/082.webp
new file mode 100644
index 00000000..ba1c6233
Binary files /dev/null and b/backend/uploads/avatars/082.webp differ
diff --git a/backend/uploads/avatars/083.webp b/backend/uploads/avatars/083.webp
new file mode 100644
index 00000000..f4f8346f
Binary files /dev/null and b/backend/uploads/avatars/083.webp differ
diff --git a/backend/uploads/avatars/084.webp b/backend/uploads/avatars/084.webp
new file mode 100644
index 00000000..fcdf8a0b
Binary files /dev/null and b/backend/uploads/avatars/084.webp differ
diff --git a/backend/uploads/avatars/085.webp b/backend/uploads/avatars/085.webp
new file mode 100644
index 00000000..d1ef747e
Binary files /dev/null and b/backend/uploads/avatars/085.webp differ
diff --git a/backend/uploads/avatars/086.webp b/backend/uploads/avatars/086.webp
new file mode 100644
index 00000000..9dedd650
Binary files /dev/null and b/backend/uploads/avatars/086.webp differ
diff --git a/backend/uploads/avatars/087.webp b/backend/uploads/avatars/087.webp
new file mode 100644
index 00000000..b1717ba5
Binary files /dev/null and b/backend/uploads/avatars/087.webp differ
diff --git a/backend/uploads/avatars/088.webp b/backend/uploads/avatars/088.webp
new file mode 100644
index 00000000..f3ceca9d
Binary files /dev/null and b/backend/uploads/avatars/088.webp differ
diff --git a/backend/uploads/avatars/089.webp b/backend/uploads/avatars/089.webp
new file mode 100644
index 00000000..ba03d78d
Binary files /dev/null and b/backend/uploads/avatars/089.webp differ
diff --git a/backend/uploads/avatars/090.webp b/backend/uploads/avatars/090.webp
new file mode 100644
index 00000000..cc609ba6
Binary files /dev/null and b/backend/uploads/avatars/090.webp differ
diff --git a/backend/uploads/avatars/091.webp b/backend/uploads/avatars/091.webp
new file mode 100644
index 00000000..aefb3482
Binary files /dev/null and b/backend/uploads/avatars/091.webp differ
diff --git a/backend/uploads/avatars/092.webp b/backend/uploads/avatars/092.webp
new file mode 100644
index 00000000..c207cfd8
Binary files /dev/null and b/backend/uploads/avatars/092.webp differ
diff --git a/backend/uploads/avatars/093.webp b/backend/uploads/avatars/093.webp
new file mode 100644
index 00000000..f604e882
Binary files /dev/null and b/backend/uploads/avatars/093.webp differ
diff --git a/backend/uploads/avatars/094.webp b/backend/uploads/avatars/094.webp
new file mode 100644
index 00000000..4338d215
Binary files /dev/null and b/backend/uploads/avatars/094.webp differ
diff --git a/backend/uploads/avatars/095.webp b/backend/uploads/avatars/095.webp
new file mode 100644
index 00000000..5241bfb6
Binary files /dev/null and b/backend/uploads/avatars/095.webp differ
diff --git a/backend/uploads/avatars/096.webp b/backend/uploads/avatars/096.webp
new file mode 100644
index 00000000..03d95d1e
Binary files /dev/null and b/backend/uploads/avatars/096.webp differ
diff --git a/backend/uploads/avatars/097.webp b/backend/uploads/avatars/097.webp
new file mode 100644
index 00000000..6baf7962
Binary files /dev/null and b/backend/uploads/avatars/097.webp differ
diff --git a/backend/uploads/avatars/098.webp b/backend/uploads/avatars/098.webp
new file mode 100644
index 00000000..5c7af65a
Binary files /dev/null and b/backend/uploads/avatars/098.webp differ
diff --git a/backend/uploads/avatars/099.webp b/backend/uploads/avatars/099.webp
new file mode 100644
index 00000000..cf5d6829
Binary files /dev/null and b/backend/uploads/avatars/099.webp differ
diff --git a/backend/uploads/avatars/100.webp b/backend/uploads/avatars/100.webp
new file mode 100644
index 00000000..49da3d4e
Binary files /dev/null and b/backend/uploads/avatars/100.webp differ
diff --git a/backend/uploads/avatars/bot.webp b/backend/uploads/avatars/bot.webp
new file mode 100644
index 00000000..a92f6868
Binary files /dev/null and b/backend/uploads/avatars/bot.webp differ
diff --git a/backend/uploads/pets/001.webp b/backend/uploads/pets/001.webp
new file mode 100644
index 00000000..ccd88582
Binary files /dev/null and b/backend/uploads/pets/001.webp differ
diff --git a/backend/uploads/pets/002.webp b/backend/uploads/pets/002.webp
new file mode 100644
index 00000000..be1f7bc3
Binary files /dev/null and b/backend/uploads/pets/002.webp differ
diff --git a/backend/uploads/pets/003.webp b/backend/uploads/pets/003.webp
new file mode 100644
index 00000000..359974e6
Binary files /dev/null and b/backend/uploads/pets/003.webp differ
diff --git a/backend/uploads/pets/004.webp b/backend/uploads/pets/004.webp
new file mode 100644
index 00000000..39d34b31
Binary files /dev/null and b/backend/uploads/pets/004.webp differ
diff --git a/backend/uploads/pets/005.webp b/backend/uploads/pets/005.webp
new file mode 100644
index 00000000..34624a76
Binary files /dev/null and b/backend/uploads/pets/005.webp differ
diff --git a/backend/uploads/pets/006.webp b/backend/uploads/pets/006.webp
new file mode 100644
index 00000000..6b79b9aa
Binary files /dev/null and b/backend/uploads/pets/006.webp differ
diff --git a/backend/uploads/pets/007.webp b/backend/uploads/pets/007.webp
new file mode 100644
index 00000000..b678783e
Binary files /dev/null and b/backend/uploads/pets/007.webp differ
diff --git a/backend/uploads/pets/008.webp b/backend/uploads/pets/008.webp
new file mode 100644
index 00000000..cbd70aff
Binary files /dev/null and b/backend/uploads/pets/008.webp differ
diff --git a/backend/uploads/pets/009.webp b/backend/uploads/pets/009.webp
new file mode 100644
index 00000000..ed0e9f5f
Binary files /dev/null and b/backend/uploads/pets/009.webp differ
diff --git a/backend/uploads/pets/010.webp b/backend/uploads/pets/010.webp
new file mode 100644
index 00000000..fc11c05f
Binary files /dev/null and b/backend/uploads/pets/010.webp differ
diff --git a/backend/uploads/pets/011.webp b/backend/uploads/pets/011.webp
new file mode 100644
index 00000000..14c0a91a
Binary files /dev/null and b/backend/uploads/pets/011.webp differ
diff --git a/backend/uploads/pets/012.webp b/backend/uploads/pets/012.webp
new file mode 100644
index 00000000..f7f407d2
Binary files /dev/null and b/backend/uploads/pets/012.webp differ
diff --git a/backend/uploads/pets/013.webp b/backend/uploads/pets/013.webp
new file mode 100644
index 00000000..daf369db
Binary files /dev/null and b/backend/uploads/pets/013.webp differ
diff --git a/backend/uploads/pets/014.webp b/backend/uploads/pets/014.webp
new file mode 100644
index 00000000..33b9ac2c
Binary files /dev/null and b/backend/uploads/pets/014.webp differ
diff --git a/backend/uploads/pets/015.webp b/backend/uploads/pets/015.webp
new file mode 100644
index 00000000..b3354d63
Binary files /dev/null and b/backend/uploads/pets/015.webp differ
diff --git a/backend/uploads/pets/016.webp b/backend/uploads/pets/016.webp
new file mode 100644
index 00000000..2c21cea6
Binary files /dev/null and b/backend/uploads/pets/016.webp differ
diff --git a/backend/uploads/pets/017.webp b/backend/uploads/pets/017.webp
new file mode 100644
index 00000000..b04d98d3
Binary files /dev/null and b/backend/uploads/pets/017.webp differ
diff --git a/backend/uploads/pets/018.webp b/backend/uploads/pets/018.webp
new file mode 100644
index 00000000..e378006e
Binary files /dev/null and b/backend/uploads/pets/018.webp differ
diff --git a/backend/uploads/pets/019.webp b/backend/uploads/pets/019.webp
new file mode 100644
index 00000000..d9335c9f
Binary files /dev/null and b/backend/uploads/pets/019.webp differ
diff --git a/backend/uploads/pets/020.webp b/backend/uploads/pets/020.webp
new file mode 100644
index 00000000..df4f9c3f
Binary files /dev/null and b/backend/uploads/pets/020.webp differ
diff --git a/backend/uploads/pets/021.webp b/backend/uploads/pets/021.webp
new file mode 100644
index 00000000..f9d97399
Binary files /dev/null and b/backend/uploads/pets/021.webp differ
diff --git a/backend/uploads/pets/022.webp b/backend/uploads/pets/022.webp
new file mode 100644
index 00000000..07695381
Binary files /dev/null and b/backend/uploads/pets/022.webp differ
diff --git a/backend/uploads/pets/023.webp b/backend/uploads/pets/023.webp
new file mode 100644
index 00000000..400117d4
Binary files /dev/null and b/backend/uploads/pets/023.webp differ
diff --git a/backend/uploads/pets/024.webp b/backend/uploads/pets/024.webp
new file mode 100644
index 00000000..b65df01e
Binary files /dev/null and b/backend/uploads/pets/024.webp differ
diff --git a/backend/uploads/pets/025.webp b/backend/uploads/pets/025.webp
new file mode 100644
index 00000000..654875cd
Binary files /dev/null and b/backend/uploads/pets/025.webp differ
diff --git a/backend/uploads/pets/026.webp b/backend/uploads/pets/026.webp
new file mode 100644
index 00000000..f38a3fc5
Binary files /dev/null and b/backend/uploads/pets/026.webp differ
diff --git a/backend/uploads/pets/027.webp b/backend/uploads/pets/027.webp
new file mode 100644
index 00000000..c124b1e5
Binary files /dev/null and b/backend/uploads/pets/027.webp differ
diff --git a/backend/uploads/pets/028.webp b/backend/uploads/pets/028.webp
new file mode 100644
index 00000000..4d7abc21
Binary files /dev/null and b/backend/uploads/pets/028.webp differ
diff --git a/backend/uploads/pets/029.webp b/backend/uploads/pets/029.webp
new file mode 100644
index 00000000..89370b50
Binary files /dev/null and b/backend/uploads/pets/029.webp differ
diff --git a/backend/uploads/pets/030.webp b/backend/uploads/pets/030.webp
new file mode 100644
index 00000000..fd4fe5ee
Binary files /dev/null and b/backend/uploads/pets/030.webp differ
diff --git a/backend/uploads/pets/031.webp b/backend/uploads/pets/031.webp
new file mode 100644
index 00000000..b95424a3
Binary files /dev/null and b/backend/uploads/pets/031.webp differ
diff --git a/backend/uploads/pets/032.webp b/backend/uploads/pets/032.webp
new file mode 100644
index 00000000..d83942d8
Binary files /dev/null and b/backend/uploads/pets/032.webp differ
diff --git a/backend/uploads/pets/033.webp b/backend/uploads/pets/033.webp
new file mode 100644
index 00000000..b1b0da10
Binary files /dev/null and b/backend/uploads/pets/033.webp differ
diff --git a/backend/uploads/pets/034.webp b/backend/uploads/pets/034.webp
new file mode 100644
index 00000000..98011b7a
Binary files /dev/null and b/backend/uploads/pets/034.webp differ
diff --git a/backend/uploads/pets/035.webp b/backend/uploads/pets/035.webp
new file mode 100644
index 00000000..b05934fc
Binary files /dev/null and b/backend/uploads/pets/035.webp differ
diff --git a/backend/uploads/pets/036.webp b/backend/uploads/pets/036.webp
new file mode 100644
index 00000000..692707f7
Binary files /dev/null and b/backend/uploads/pets/036.webp differ
diff --git a/backend/uploads/pets/037.webp b/backend/uploads/pets/037.webp
new file mode 100644
index 00000000..5443ca8d
Binary files /dev/null and b/backend/uploads/pets/037.webp differ
diff --git a/backend/uploads/pets/038.webp b/backend/uploads/pets/038.webp
new file mode 100644
index 00000000..d396c70f
Binary files /dev/null and b/backend/uploads/pets/038.webp differ
diff --git a/backend/uploads/pets/039.webp b/backend/uploads/pets/039.webp
new file mode 100644
index 00000000..74b4b14f
Binary files /dev/null and b/backend/uploads/pets/039.webp differ
diff --git a/backend/uploads/pets/040.webp b/backend/uploads/pets/040.webp
new file mode 100644
index 00000000..5d304262
Binary files /dev/null and b/backend/uploads/pets/040.webp differ
diff --git a/backend/uploads/pets/041.webp b/backend/uploads/pets/041.webp
new file mode 100644
index 00000000..3ccb008e
Binary files /dev/null and b/backend/uploads/pets/041.webp differ
diff --git a/backend/uploads/pets/042.webp b/backend/uploads/pets/042.webp
new file mode 100644
index 00000000..1d76285a
Binary files /dev/null and b/backend/uploads/pets/042.webp differ
diff --git a/backend/uploads/pets/043.webp b/backend/uploads/pets/043.webp
new file mode 100644
index 00000000..413e44f9
Binary files /dev/null and b/backend/uploads/pets/043.webp differ
diff --git a/backend/uploads/pets/044.webp b/backend/uploads/pets/044.webp
new file mode 100644
index 00000000..b99a611f
Binary files /dev/null and b/backend/uploads/pets/044.webp differ
diff --git a/backend/uploads/pets/045.webp b/backend/uploads/pets/045.webp
new file mode 100644
index 00000000..3cad5e6e
Binary files /dev/null and b/backend/uploads/pets/045.webp differ
diff --git a/backend/uploads/pets/046.webp b/backend/uploads/pets/046.webp
new file mode 100644
index 00000000..e39d1f14
Binary files /dev/null and b/backend/uploads/pets/046.webp differ
diff --git a/backend/uploads/pets/047.webp b/backend/uploads/pets/047.webp
new file mode 100644
index 00000000..f929a0ce
Binary files /dev/null and b/backend/uploads/pets/047.webp differ
diff --git a/backend/uploads/pets/048.webp b/backend/uploads/pets/048.webp
new file mode 100644
index 00000000..01cc52f8
Binary files /dev/null and b/backend/uploads/pets/048.webp differ
diff --git a/backend/uploads/pets/049.webp b/backend/uploads/pets/049.webp
new file mode 100644
index 00000000..3cea07b5
Binary files /dev/null and b/backend/uploads/pets/049.webp differ
diff --git a/backend/uploads/pets/050.webp b/backend/uploads/pets/050.webp
new file mode 100644
index 00000000..079886db
Binary files /dev/null and b/backend/uploads/pets/050.webp differ
diff --git a/backend/uploads/pets/051.webp b/backend/uploads/pets/051.webp
new file mode 100644
index 00000000..5e683607
Binary files /dev/null and b/backend/uploads/pets/051.webp differ
diff --git a/backend/uploads/pets/052.webp b/backend/uploads/pets/052.webp
new file mode 100644
index 00000000..6c334394
Binary files /dev/null and b/backend/uploads/pets/052.webp differ
diff --git a/backend/uploads/pets/053.webp b/backend/uploads/pets/053.webp
new file mode 100644
index 00000000..b9d61bbd
Binary files /dev/null and b/backend/uploads/pets/053.webp differ
diff --git a/backend/uploads/pets/054.webp b/backend/uploads/pets/054.webp
new file mode 100644
index 00000000..ee98ced1
Binary files /dev/null and b/backend/uploads/pets/054.webp differ
diff --git a/backend/uploads/pets/055.webp b/backend/uploads/pets/055.webp
new file mode 100644
index 00000000..084af9a1
Binary files /dev/null and b/backend/uploads/pets/055.webp differ
diff --git a/backend/uploads/pets/056.webp b/backend/uploads/pets/056.webp
new file mode 100644
index 00000000..66facf07
Binary files /dev/null and b/backend/uploads/pets/056.webp differ
diff --git a/backend/uploads/pets/057.webp b/backend/uploads/pets/057.webp
new file mode 100644
index 00000000..4f6b3b51
Binary files /dev/null and b/backend/uploads/pets/057.webp differ
diff --git a/backend/uploads/pets/058.webp b/backend/uploads/pets/058.webp
new file mode 100644
index 00000000..430f5c4a
Binary files /dev/null and b/backend/uploads/pets/058.webp differ
diff --git a/backend/uploads/pets/059.webp b/backend/uploads/pets/059.webp
new file mode 100644
index 00000000..23b0f85a
Binary files /dev/null and b/backend/uploads/pets/059.webp differ
diff --git a/backend/uploads/pets/060.webp b/backend/uploads/pets/060.webp
new file mode 100644
index 00000000..5123f7ae
Binary files /dev/null and b/backend/uploads/pets/060.webp differ
diff --git a/backend/uploads/pets/061.webp b/backend/uploads/pets/061.webp
new file mode 100644
index 00000000..a04e7446
Binary files /dev/null and b/backend/uploads/pets/061.webp differ
diff --git a/backend/uploads/pets/062.webp b/backend/uploads/pets/062.webp
new file mode 100644
index 00000000..1652a575
Binary files /dev/null and b/backend/uploads/pets/062.webp differ
diff --git a/backend/uploads/pets/063.webp b/backend/uploads/pets/063.webp
new file mode 100644
index 00000000..99e93a86
Binary files /dev/null and b/backend/uploads/pets/063.webp differ
diff --git a/backend/uploads/pets/064.webp b/backend/uploads/pets/064.webp
new file mode 100644
index 00000000..ce7e185c
Binary files /dev/null and b/backend/uploads/pets/064.webp differ
diff --git a/backend/uploads/pets/065.webp b/backend/uploads/pets/065.webp
new file mode 100644
index 00000000..5864ca98
Binary files /dev/null and b/backend/uploads/pets/065.webp differ
diff --git a/backend/uploads/pets/066.webp b/backend/uploads/pets/066.webp
new file mode 100644
index 00000000..3771ccd4
Binary files /dev/null and b/backend/uploads/pets/066.webp differ
diff --git a/backend/uploads/pets/067.webp b/backend/uploads/pets/067.webp
new file mode 100644
index 00000000..21b2292a
Binary files /dev/null and b/backend/uploads/pets/067.webp differ
diff --git a/backend/uploads/pets/068.webp b/backend/uploads/pets/068.webp
new file mode 100644
index 00000000..6f262352
Binary files /dev/null and b/backend/uploads/pets/068.webp differ
diff --git a/backend/uploads/pets/069.webp b/backend/uploads/pets/069.webp
new file mode 100644
index 00000000..107c3f49
Binary files /dev/null and b/backend/uploads/pets/069.webp differ
diff --git a/backend/uploads/pets/070.webp b/backend/uploads/pets/070.webp
new file mode 100644
index 00000000..21699aa4
Binary files /dev/null and b/backend/uploads/pets/070.webp differ
diff --git a/backend/uploads/pets/071.webp b/backend/uploads/pets/071.webp
new file mode 100644
index 00000000..ff14840a
Binary files /dev/null and b/backend/uploads/pets/071.webp differ
diff --git a/backend/uploads/pets/072.webp b/backend/uploads/pets/072.webp
new file mode 100644
index 00000000..c730848c
Binary files /dev/null and b/backend/uploads/pets/072.webp differ
diff --git a/backend/uploads/pets/073.webp b/backend/uploads/pets/073.webp
new file mode 100644
index 00000000..daaa276c
Binary files /dev/null and b/backend/uploads/pets/073.webp differ
diff --git a/backend/uploads/pets/074.webp b/backend/uploads/pets/074.webp
new file mode 100644
index 00000000..23b76888
Binary files /dev/null and b/backend/uploads/pets/074.webp differ
diff --git a/backend/uploads/pets/075.webp b/backend/uploads/pets/075.webp
new file mode 100644
index 00000000..4262db1a
Binary files /dev/null and b/backend/uploads/pets/075.webp differ
diff --git a/backend/uploads/pets/076.webp b/backend/uploads/pets/076.webp
new file mode 100644
index 00000000..08d5b420
Binary files /dev/null and b/backend/uploads/pets/076.webp differ
diff --git a/backend/uploads/pets/077.webp b/backend/uploads/pets/077.webp
new file mode 100644
index 00000000..ad03239d
Binary files /dev/null and b/backend/uploads/pets/077.webp differ
diff --git a/backend/uploads/pets/078.webp b/backend/uploads/pets/078.webp
new file mode 100644
index 00000000..1001add1
Binary files /dev/null and b/backend/uploads/pets/078.webp differ
diff --git a/backend/uploads/pets/079.webp b/backend/uploads/pets/079.webp
new file mode 100644
index 00000000..e92085e1
Binary files /dev/null and b/backend/uploads/pets/079.webp differ
diff --git a/backend/uploads/pets/080.webp b/backend/uploads/pets/080.webp
new file mode 100644
index 00000000..bcaef257
Binary files /dev/null and b/backend/uploads/pets/080.webp differ
diff --git a/backend/uploads/pets/081.webp b/backend/uploads/pets/081.webp
new file mode 100644
index 00000000..6f0e0642
Binary files /dev/null and b/backend/uploads/pets/081.webp differ
diff --git a/backend/uploads/pets/082.webp b/backend/uploads/pets/082.webp
new file mode 100644
index 00000000..eab2b19a
Binary files /dev/null and b/backend/uploads/pets/082.webp differ
diff --git a/backend/uploads/pets/083.webp b/backend/uploads/pets/083.webp
new file mode 100644
index 00000000..684eb290
Binary files /dev/null and b/backend/uploads/pets/083.webp differ
diff --git a/backend/uploads/pets/084.webp b/backend/uploads/pets/084.webp
new file mode 100644
index 00000000..4b7ff963
Binary files /dev/null and b/backend/uploads/pets/084.webp differ
diff --git a/backend/uploads/pets/085.webp b/backend/uploads/pets/085.webp
new file mode 100644
index 00000000..1ac48995
Binary files /dev/null and b/backend/uploads/pets/085.webp differ
diff --git a/backend/uploads/pets/086.webp b/backend/uploads/pets/086.webp
new file mode 100644
index 00000000..ff288d54
Binary files /dev/null and b/backend/uploads/pets/086.webp differ
diff --git a/backend/uploads/pets/087.webp b/backend/uploads/pets/087.webp
new file mode 100644
index 00000000..d46a1dcb
Binary files /dev/null and b/backend/uploads/pets/087.webp differ
diff --git a/backend/uploads/pets/088.webp b/backend/uploads/pets/088.webp
new file mode 100644
index 00000000..2008bf53
Binary files /dev/null and b/backend/uploads/pets/088.webp differ
diff --git a/backend/uploads/pets/089.webp b/backend/uploads/pets/089.webp
new file mode 100644
index 00000000..245e35e1
Binary files /dev/null and b/backend/uploads/pets/089.webp differ
diff --git a/backend/uploads/pets/090.webp b/backend/uploads/pets/090.webp
new file mode 100644
index 00000000..7ad13f23
Binary files /dev/null and b/backend/uploads/pets/090.webp differ
diff --git a/backend/uploads/pets/091.webp b/backend/uploads/pets/091.webp
new file mode 100644
index 00000000..d81bbe09
Binary files /dev/null and b/backend/uploads/pets/091.webp differ
diff --git a/backend/uploads/pets/092.webp b/backend/uploads/pets/092.webp
new file mode 100644
index 00000000..14ea5315
Binary files /dev/null and b/backend/uploads/pets/092.webp differ
diff --git a/backend/uploads/pets/093.webp b/backend/uploads/pets/093.webp
new file mode 100644
index 00000000..c8ed2ee6
Binary files /dev/null and b/backend/uploads/pets/093.webp differ
diff --git a/backend/uploads/pets/094.webp b/backend/uploads/pets/094.webp
new file mode 100644
index 00000000..4a36d164
Binary files /dev/null and b/backend/uploads/pets/094.webp differ
diff --git a/backend/uploads/pets/095.webp b/backend/uploads/pets/095.webp
new file mode 100644
index 00000000..07abe07a
Binary files /dev/null and b/backend/uploads/pets/095.webp differ
diff --git a/backend/uploads/pets/096.webp b/backend/uploads/pets/096.webp
new file mode 100644
index 00000000..87f17555
Binary files /dev/null and b/backend/uploads/pets/096.webp differ
diff --git a/backend/uploads/pets/097.webp b/backend/uploads/pets/097.webp
new file mode 100644
index 00000000..9a70552b
Binary files /dev/null and b/backend/uploads/pets/097.webp differ
diff --git a/backend/uploads/pets/098.webp b/backend/uploads/pets/098.webp
new file mode 100644
index 00000000..12e68fb0
Binary files /dev/null and b/backend/uploads/pets/098.webp differ
diff --git a/backend/uploads/pets/099.webp b/backend/uploads/pets/099.webp
new file mode 100644
index 00000000..320b19a3
Binary files /dev/null and b/backend/uploads/pets/099.webp differ
diff --git a/backend/uploads/pets/100.webp b/backend/uploads/pets/100.webp
new file mode 100644
index 00000000..7363d204
Binary files /dev/null and b/backend/uploads/pets/100.webp differ
diff --git a/backend/uploads/products/001.webp b/backend/uploads/products/001.webp
new file mode 100644
index 00000000..a77c1a7f
Binary files /dev/null and b/backend/uploads/products/001.webp differ
diff --git a/backend/uploads/products/002.webp b/backend/uploads/products/002.webp
new file mode 100644
index 00000000..e83cb558
Binary files /dev/null and b/backend/uploads/products/002.webp differ
diff --git a/backend/uploads/products/003.webp b/backend/uploads/products/003.webp
new file mode 100644
index 00000000..6775cd42
Binary files /dev/null and b/backend/uploads/products/003.webp differ
diff --git a/backend/uploads/products/004.webp b/backend/uploads/products/004.webp
new file mode 100644
index 00000000..bf82fce3
Binary files /dev/null and b/backend/uploads/products/004.webp differ
diff --git a/backend/uploads/products/005.webp b/backend/uploads/products/005.webp
new file mode 100644
index 00000000..15788d23
Binary files /dev/null and b/backend/uploads/products/005.webp differ
diff --git a/backend/uploads/products/006.webp b/backend/uploads/products/006.webp
new file mode 100644
index 00000000..3ee12fb8
Binary files /dev/null and b/backend/uploads/products/006.webp differ
diff --git a/backend/uploads/products/007.webp b/backend/uploads/products/007.webp
new file mode 100644
index 00000000..c6fe28b5
Binary files /dev/null and b/backend/uploads/products/007.webp differ
diff --git a/backend/uploads/products/008.webp b/backend/uploads/products/008.webp
new file mode 100644
index 00000000..8535d69d
Binary files /dev/null and b/backend/uploads/products/008.webp differ
diff --git a/backend/uploads/products/009.webp b/backend/uploads/products/009.webp
new file mode 100644
index 00000000..acf2c1af
Binary files /dev/null and b/backend/uploads/products/009.webp differ
diff --git a/backend/uploads/products/010.webp b/backend/uploads/products/010.webp
new file mode 100644
index 00000000..bb0e238b
Binary files /dev/null and b/backend/uploads/products/010.webp differ
diff --git a/backend/uploads/products/011.webp b/backend/uploads/products/011.webp
new file mode 100644
index 00000000..ec8122c4
Binary files /dev/null and b/backend/uploads/products/011.webp differ
diff --git a/backend/uploads/products/012.webp b/backend/uploads/products/012.webp
new file mode 100644
index 00000000..ff2641d2
Binary files /dev/null and b/backend/uploads/products/012.webp differ
diff --git a/backend/uploads/products/013.webp b/backend/uploads/products/013.webp
new file mode 100644
index 00000000..5e70b2c1
Binary files /dev/null and b/backend/uploads/products/013.webp differ
diff --git a/backend/uploads/products/014.webp b/backend/uploads/products/014.webp
new file mode 100644
index 00000000..06ab3918
Binary files /dev/null and b/backend/uploads/products/014.webp differ
diff --git a/backend/uploads/products/015.webp b/backend/uploads/products/015.webp
new file mode 100644
index 00000000..c966feb9
Binary files /dev/null and b/backend/uploads/products/015.webp differ
diff --git a/backend/uploads/products/016.webp b/backend/uploads/products/016.webp
new file mode 100644
index 00000000..ba84a79e
Binary files /dev/null and b/backend/uploads/products/016.webp differ
diff --git a/backend/uploads/products/017.webp b/backend/uploads/products/017.webp
new file mode 100644
index 00000000..95c20edb
Binary files /dev/null and b/backend/uploads/products/017.webp differ
diff --git a/backend/uploads/products/018.webp b/backend/uploads/products/018.webp
new file mode 100644
index 00000000..4d80ee5a
Binary files /dev/null and b/backend/uploads/products/018.webp differ
diff --git a/backend/uploads/products/019.webp b/backend/uploads/products/019.webp
new file mode 100644
index 00000000..0ec3b2a4
Binary files /dev/null and b/backend/uploads/products/019.webp differ
diff --git a/backend/uploads/products/020.webp b/backend/uploads/products/020.webp
new file mode 100644
index 00000000..3f33062d
Binary files /dev/null and b/backend/uploads/products/020.webp differ
diff --git a/backend/uploads/products/021.webp b/backend/uploads/products/021.webp
new file mode 100644
index 00000000..9ae973ca
Binary files /dev/null and b/backend/uploads/products/021.webp differ
diff --git a/backend/uploads/products/022.webp b/backend/uploads/products/022.webp
new file mode 100644
index 00000000..d4b076bd
Binary files /dev/null and b/backend/uploads/products/022.webp differ
diff --git a/backend/uploads/products/023.webp b/backend/uploads/products/023.webp
new file mode 100644
index 00000000..b25ff3ec
Binary files /dev/null and b/backend/uploads/products/023.webp differ
diff --git a/backend/uploads/products/024.webp b/backend/uploads/products/024.webp
new file mode 100644
index 00000000..75988867
Binary files /dev/null and b/backend/uploads/products/024.webp differ
diff --git a/backend/uploads/products/025.webp b/backend/uploads/products/025.webp
new file mode 100644
index 00000000..7aa4b93e
Binary files /dev/null and b/backend/uploads/products/025.webp differ
diff --git a/backend/uploads/products/026.webp b/backend/uploads/products/026.webp
new file mode 100644
index 00000000..b401bbe3
Binary files /dev/null and b/backend/uploads/products/026.webp differ
diff --git a/backend/uploads/products/027.webp b/backend/uploads/products/027.webp
new file mode 100644
index 00000000..d5d2a797
Binary files /dev/null and b/backend/uploads/products/027.webp differ
diff --git a/backend/uploads/products/028.webp b/backend/uploads/products/028.webp
new file mode 100644
index 00000000..7c2d739e
Binary files /dev/null and b/backend/uploads/products/028.webp differ
diff --git a/backend/uploads/products/029.webp b/backend/uploads/products/029.webp
new file mode 100644
index 00000000..6956e5ae
Binary files /dev/null and b/backend/uploads/products/029.webp differ
diff --git a/backend/uploads/products/030.webp b/backend/uploads/products/030.webp
new file mode 100644
index 00000000..e25dc6ac
Binary files /dev/null and b/backend/uploads/products/030.webp differ
diff --git a/backend/uploads/products/031.webp b/backend/uploads/products/031.webp
new file mode 100644
index 00000000..cc8e80e9
Binary files /dev/null and b/backend/uploads/products/031.webp differ
diff --git a/backend/uploads/products/032.webp b/backend/uploads/products/032.webp
new file mode 100644
index 00000000..ae44bf67
Binary files /dev/null and b/backend/uploads/products/032.webp differ
diff --git a/backend/uploads/products/033.webp b/backend/uploads/products/033.webp
new file mode 100644
index 00000000..3bbd6036
Binary files /dev/null and b/backend/uploads/products/033.webp differ
diff --git a/backend/uploads/products/034.webp b/backend/uploads/products/034.webp
new file mode 100644
index 00000000..9858dd89
Binary files /dev/null and b/backend/uploads/products/034.webp differ
diff --git a/backend/uploads/products/035.webp b/backend/uploads/products/035.webp
new file mode 100644
index 00000000..d9444061
Binary files /dev/null and b/backend/uploads/products/035.webp differ
diff --git a/backend/uploads/products/036.webp b/backend/uploads/products/036.webp
new file mode 100644
index 00000000..5bd3632d
Binary files /dev/null and b/backend/uploads/products/036.webp differ
diff --git a/backend/uploads/products/037.webp b/backend/uploads/products/037.webp
new file mode 100644
index 00000000..99035bd8
Binary files /dev/null and b/backend/uploads/products/037.webp differ
diff --git a/backend/uploads/products/038.webp b/backend/uploads/products/038.webp
new file mode 100644
index 00000000..8cfb8230
Binary files /dev/null and b/backend/uploads/products/038.webp differ
diff --git a/backend/uploads/products/039.webp b/backend/uploads/products/039.webp
new file mode 100644
index 00000000..1c0ddf56
Binary files /dev/null and b/backend/uploads/products/039.webp differ
diff --git a/backend/uploads/products/040.webp b/backend/uploads/products/040.webp
new file mode 100644
index 00000000..117595f8
Binary files /dev/null and b/backend/uploads/products/040.webp differ
diff --git a/backend/uploads/products/041.webp b/backend/uploads/products/041.webp
new file mode 100644
index 00000000..57eba1fa
Binary files /dev/null and b/backend/uploads/products/041.webp differ
diff --git a/backend/uploads/products/042.webp b/backend/uploads/products/042.webp
new file mode 100644
index 00000000..bfa2aba1
Binary files /dev/null and b/backend/uploads/products/042.webp differ
diff --git a/backend/uploads/products/043.webp b/backend/uploads/products/043.webp
new file mode 100644
index 00000000..226358f0
Binary files /dev/null and b/backend/uploads/products/043.webp differ
diff --git a/backend/uploads/products/044.webp b/backend/uploads/products/044.webp
new file mode 100644
index 00000000..f46f13b4
Binary files /dev/null and b/backend/uploads/products/044.webp differ
diff --git a/backend/uploads/products/045.webp b/backend/uploads/products/045.webp
new file mode 100644
index 00000000..92b348b0
Binary files /dev/null and b/backend/uploads/products/045.webp differ
diff --git a/backend/uploads/products/046.webp b/backend/uploads/products/046.webp
new file mode 100644
index 00000000..4f797232
Binary files /dev/null and b/backend/uploads/products/046.webp differ
diff --git a/backend/uploads/products/047.webp b/backend/uploads/products/047.webp
new file mode 100644
index 00000000..4b04eba5
Binary files /dev/null and b/backend/uploads/products/047.webp differ
diff --git a/backend/uploads/products/048.webp b/backend/uploads/products/048.webp
new file mode 100644
index 00000000..20e60743
Binary files /dev/null and b/backend/uploads/products/048.webp differ
diff --git a/backend/uploads/products/049.webp b/backend/uploads/products/049.webp
new file mode 100644
index 00000000..7517f9b0
Binary files /dev/null and b/backend/uploads/products/049.webp differ
diff --git a/backend/uploads/products/050.webp b/backend/uploads/products/050.webp
new file mode 100644
index 00000000..f3e04000
Binary files /dev/null and b/backend/uploads/products/050.webp differ
diff --git a/backend/uploads/products/051.webp b/backend/uploads/products/051.webp
new file mode 100644
index 00000000..59843fe7
Binary files /dev/null and b/backend/uploads/products/051.webp differ
diff --git a/backend/uploads/products/052.webp b/backend/uploads/products/052.webp
new file mode 100644
index 00000000..358bfdb6
Binary files /dev/null and b/backend/uploads/products/052.webp differ
diff --git a/backend/uploads/products/053.webp b/backend/uploads/products/053.webp
new file mode 100644
index 00000000..0be3d445
Binary files /dev/null and b/backend/uploads/products/053.webp differ
diff --git a/backend/uploads/products/054.webp b/backend/uploads/products/054.webp
new file mode 100644
index 00000000..d23032f5
Binary files /dev/null and b/backend/uploads/products/054.webp differ
diff --git a/backend/uploads/products/055.webp b/backend/uploads/products/055.webp
new file mode 100644
index 00000000..a4ceba73
Binary files /dev/null and b/backend/uploads/products/055.webp differ
diff --git a/backend/uploads/products/056.webp b/backend/uploads/products/056.webp
new file mode 100644
index 00000000..c33eca4a
Binary files /dev/null and b/backend/uploads/products/056.webp differ
diff --git a/backend/uploads/products/057.webp b/backend/uploads/products/057.webp
new file mode 100644
index 00000000..57d7da86
Binary files /dev/null and b/backend/uploads/products/057.webp differ
diff --git a/backend/uploads/products/058.webp b/backend/uploads/products/058.webp
new file mode 100644
index 00000000..6b110093
Binary files /dev/null and b/backend/uploads/products/058.webp differ
diff --git a/backend/uploads/products/059.webp b/backend/uploads/products/059.webp
new file mode 100644
index 00000000..d6b84e2e
Binary files /dev/null and b/backend/uploads/products/059.webp differ
diff --git a/backend/uploads/products/060.webp b/backend/uploads/products/060.webp
new file mode 100644
index 00000000..1fc756da
Binary files /dev/null and b/backend/uploads/products/060.webp differ
diff --git a/backend/uploads/products/061.webp b/backend/uploads/products/061.webp
new file mode 100644
index 00000000..e9d44375
Binary files /dev/null and b/backend/uploads/products/061.webp differ
diff --git a/backend/uploads/products/062.webp b/backend/uploads/products/062.webp
new file mode 100644
index 00000000..3c62c0c7
Binary files /dev/null and b/backend/uploads/products/062.webp differ
diff --git a/backend/uploads/products/063.webp b/backend/uploads/products/063.webp
new file mode 100644
index 00000000..4ba1522f
Binary files /dev/null and b/backend/uploads/products/063.webp differ
diff --git a/backend/uploads/products/064.webp b/backend/uploads/products/064.webp
new file mode 100644
index 00000000..b90beba7
Binary files /dev/null and b/backend/uploads/products/064.webp differ
diff --git a/backend/uploads/products/065.webp b/backend/uploads/products/065.webp
new file mode 100644
index 00000000..8bf0b426
Binary files /dev/null and b/backend/uploads/products/065.webp differ
diff --git a/backend/uploads/products/066.webp b/backend/uploads/products/066.webp
new file mode 100644
index 00000000..b7e1ddfe
Binary files /dev/null and b/backend/uploads/products/066.webp differ
diff --git a/backend/uploads/products/067.webp b/backend/uploads/products/067.webp
new file mode 100644
index 00000000..bb361f2f
Binary files /dev/null and b/backend/uploads/products/067.webp differ
diff --git a/backend/uploads/products/068.webp b/backend/uploads/products/068.webp
new file mode 100644
index 00000000..06064410
Binary files /dev/null and b/backend/uploads/products/068.webp differ
diff --git a/backend/uploads/products/069.webp b/backend/uploads/products/069.webp
new file mode 100644
index 00000000..22cceb73
Binary files /dev/null and b/backend/uploads/products/069.webp differ
diff --git a/backend/uploads/products/070.webp b/backend/uploads/products/070.webp
new file mode 100644
index 00000000..62054903
Binary files /dev/null and b/backend/uploads/products/070.webp differ
diff --git a/backend/uploads/products/071.webp b/backend/uploads/products/071.webp
new file mode 100644
index 00000000..640da88c
Binary files /dev/null and b/backend/uploads/products/071.webp differ
diff --git a/backend/uploads/products/072.webp b/backend/uploads/products/072.webp
new file mode 100644
index 00000000..51f7fa89
Binary files /dev/null and b/backend/uploads/products/072.webp differ
diff --git a/backend/uploads/products/073.webp b/backend/uploads/products/073.webp
new file mode 100644
index 00000000..f07e631e
Binary files /dev/null and b/backend/uploads/products/073.webp differ
diff --git a/backend/uploads/products/074.webp b/backend/uploads/products/074.webp
new file mode 100644
index 00000000..9a1f9009
Binary files /dev/null and b/backend/uploads/products/074.webp differ
diff --git a/backend/uploads/products/075.webp b/backend/uploads/products/075.webp
new file mode 100644
index 00000000..9e8c5f4b
Binary files /dev/null and b/backend/uploads/products/075.webp differ
diff --git a/backend/uploads/products/076.webp b/backend/uploads/products/076.webp
new file mode 100644
index 00000000..f6a7ef03
Binary files /dev/null and b/backend/uploads/products/076.webp differ
diff --git a/backend/uploads/products/077.webp b/backend/uploads/products/077.webp
new file mode 100644
index 00000000..0aa81b00
Binary files /dev/null and b/backend/uploads/products/077.webp differ
diff --git a/backend/uploads/products/078.webp b/backend/uploads/products/078.webp
new file mode 100644
index 00000000..a0f729a2
Binary files /dev/null and b/backend/uploads/products/078.webp differ
diff --git a/backend/uploads/products/079.webp b/backend/uploads/products/079.webp
new file mode 100644
index 00000000..d0027a5f
Binary files /dev/null and b/backend/uploads/products/079.webp differ
diff --git a/backend/uploads/products/080.webp b/backend/uploads/products/080.webp
new file mode 100644
index 00000000..3725f3d4
Binary files /dev/null and b/backend/uploads/products/080.webp differ
diff --git a/backend/uploads/products/081.webp b/backend/uploads/products/081.webp
new file mode 100644
index 00000000..7d4bb52f
Binary files /dev/null and b/backend/uploads/products/081.webp differ
diff --git a/backend/uploads/products/082.webp b/backend/uploads/products/082.webp
new file mode 100644
index 00000000..39f6d0ea
Binary files /dev/null and b/backend/uploads/products/082.webp differ
diff --git a/backend/uploads/products/083.webp b/backend/uploads/products/083.webp
new file mode 100644
index 00000000..d600462f
Binary files /dev/null and b/backend/uploads/products/083.webp differ
diff --git a/backend/uploads/products/084.webp b/backend/uploads/products/084.webp
new file mode 100644
index 00000000..d00c2b26
Binary files /dev/null and b/backend/uploads/products/084.webp differ
diff --git a/backend/uploads/products/085.webp b/backend/uploads/products/085.webp
new file mode 100644
index 00000000..d9489ee0
Binary files /dev/null and b/backend/uploads/products/085.webp differ
diff --git a/backend/uploads/products/086.webp b/backend/uploads/products/086.webp
new file mode 100644
index 00000000..079312ab
Binary files /dev/null and b/backend/uploads/products/086.webp differ
diff --git a/backend/uploads/products/087.webp b/backend/uploads/products/087.webp
new file mode 100644
index 00000000..4a822786
Binary files /dev/null and b/backend/uploads/products/087.webp differ
diff --git a/backend/uploads/products/088.webp b/backend/uploads/products/088.webp
new file mode 100644
index 00000000..d7d5553e
Binary files /dev/null and b/backend/uploads/products/088.webp differ
diff --git a/backend/uploads/products/089.webp b/backend/uploads/products/089.webp
new file mode 100644
index 00000000..21274a49
Binary files /dev/null and b/backend/uploads/products/089.webp differ
diff --git a/backend/uploads/products/090.webp b/backend/uploads/products/090.webp
new file mode 100644
index 00000000..ab301a06
Binary files /dev/null and b/backend/uploads/products/090.webp differ
diff --git a/backend/uploads/products/091.webp b/backend/uploads/products/091.webp
new file mode 100644
index 00000000..b336a344
Binary files /dev/null and b/backend/uploads/products/091.webp differ
diff --git a/backend/uploads/products/092.webp b/backend/uploads/products/092.webp
new file mode 100644
index 00000000..6ef28af2
Binary files /dev/null and b/backend/uploads/products/092.webp differ
diff --git a/backend/uploads/products/093.webp b/backend/uploads/products/093.webp
new file mode 100644
index 00000000..73ea1e9a
Binary files /dev/null and b/backend/uploads/products/093.webp differ
diff --git a/backend/uploads/products/094.webp b/backend/uploads/products/094.webp
new file mode 100644
index 00000000..c0a5b588
Binary files /dev/null and b/backend/uploads/products/094.webp differ
diff --git a/backend/uploads/products/095.webp b/backend/uploads/products/095.webp
new file mode 100644
index 00000000..81d343cb
Binary files /dev/null and b/backend/uploads/products/095.webp differ
diff --git a/backend/uploads/products/096.webp b/backend/uploads/products/096.webp
new file mode 100644
index 00000000..50cf062a
Binary files /dev/null and b/backend/uploads/products/096.webp differ
diff --git a/backend/uploads/products/097.webp b/backend/uploads/products/097.webp
new file mode 100644
index 00000000..574f9610
Binary files /dev/null and b/backend/uploads/products/097.webp differ
diff --git a/backend/uploads/products/098.webp b/backend/uploads/products/098.webp
new file mode 100644
index 00000000..a5c5cbb0
Binary files /dev/null and b/backend/uploads/products/098.webp differ
diff --git a/backend/uploads/products/099.webp b/backend/uploads/products/099.webp
new file mode 100644
index 00000000..8ac94f20
Binary files /dev/null and b/backend/uploads/products/099.webp differ
diff --git a/backend/uploads/products/100.webp b/backend/uploads/products/100.webp
new file mode 100644
index 00000000..485919c3
Binary files /dev/null and b/backend/uploads/products/100.webp differ