From 6537e96e469b01c6af1c8717eef4a1921e3512ab Mon Sep 17 00:00:00 2001 From: Gabriel Santana de Menezes Date: Sun, 10 May 2026 12:07:35 -0400 Subject: [PATCH] chore: logging --- .../OrganizationRepositoryImpl.java | 22 +- .../repository/ProjectRepositoryImpl.java | 16 +- .../repository/TaskRepositoryImpl.java | 29 +- .../UserOrganizationRepositoryImpl.java | 24 +- .../repository/UserRepositoryImpl.java | 22 +- .../controllers/auth/AuthController.java | 9 + .../dashboard/DashboardController.java | 3 + .../organization/OrganizationController.java | 22 +- .../orgdashboard/MemberDetailController.java | 4 + .../orgdashboard/OrgDashboardController.java | 4 + .../project/ProjectController.java | 8 +- .../controllers/task/TaskController.java | 21 +- .../usecase/auth/login/LoginUseCase.java | 6 + .../auth/refresh/RefreshTokenUseCase.java | 7 + .../usecase/dashboard/DashboardUseCase.java | 9 +- .../AuthorizeOrganizationAdminUseCase.java | 8 + .../create/CreateOrganizationUseCase.java | 5 + .../join/RequestJoinOrganizationUseCase.java | 6 + .../requests/HandleJoinRequestUseCase.java | 8 + .../requests/ListPendingRequestsUseCase.java | 6 +- .../requests/ListUserJoinRequestsUseCase.java | 11 +- .../search/SearchOrganizationsUseCase.java | 6 +- .../orgdashboard/OrgDashboardUseCase.java | 9 +- .../memberdetail/MemberDetailUseCase.java | 15 +- .../project/create/CreateProjectUseCase.java | 12 +- .../project/list/ListProjectsUseCase.java | 6 +- .../task/create/CreateTaskUseCase.java | 17 +- .../usecase/task/get/GetTaskUseCase.java | 3 + .../task/listall/ListAllTasksUseCase.java | 6 +- .../task/listall/ListMyTasksUseCase.java | 6 +- .../usecase/task/start/StartTaskUseCase.java | 9 +- .../usecase/task/stop/StopTaskUseCase.java | 7 +- .../task/update/UpdateTaskStatusUseCase.java | 9 +- .../task/update/UpdateTaskUseCase.java | 18 +- .../user/create/CreateUserUseCase.java | 5 + .../user/profile/GetUserProfileUseCase.java | 4 + frontend/pnpm-lock.yaml | 2166 +++++++++++++++++ 37 files changed, 2497 insertions(+), 51 deletions(-) create mode 100644 frontend/pnpm-lock.yaml diff --git a/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/repository/OrganizationRepositoryImpl.java b/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/repository/OrganizationRepositoryImpl.java index 037fd89..a4e9209 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/repository/OrganizationRepositoryImpl.java +++ b/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/repository/OrganizationRepositoryImpl.java @@ -7,6 +7,7 @@ import net.beetechgroup.beetask.entities.organization.Organization; import net.beetechgroup.beetask.frameworks.persistence.entities.OrganizationEntity; import net.beetechgroup.beetask.frameworks.persistence.mapper.OrganizationEntityMapper; import net.beetechgroup.beetask.usecase.repository.OrganizationRepository; +import org.jboss.logging.Logger; import java.util.List; import java.util.Objects; @@ -14,6 +15,7 @@ import java.util.Optional; @ApplicationScoped public class OrganizationRepositoryImpl implements OrganizationRepository, PanacheRepository { + private static final Logger LOGGER = Logger.getLogger(OrganizationRepositoryImpl.class); @Override @Transactional @@ -24,25 +26,35 @@ public class OrganizationRepositoryImpl implements OrganizationRepository, Panac } else { entity = getEntityManager().merge(entity); } - return OrganizationEntityMapper.toDomain(entity); + Organization savedOrganization = OrganizationEntityMapper.toDomain(entity); + LOGGER.infof("Persisted organization %d with name '%s'", savedOrganization.getId(), savedOrganization.getName()); + return savedOrganization; } @Override public List search(String query) { if (Objects.isNull(query) || query.isBlank()) { - return findAll().list().stream() + List organizations = findAll().list().stream() .map(OrganizationEntityMapper::toDomain) .toList(); + LOGGER.infof("Loaded %d organizations with empty search query", organizations.size()); + return organizations; } - return find("name like ?1", "%" + query + "%").stream() + List organizations = find("name like ?1", "%" + query + "%").stream() .map(OrganizationEntityMapper::toDomain) .toList(); + LOGGER.infof("Loaded %d organizations for search query '%s'", organizations.size(), query); + return organizations; } @Override public Optional findOrganizationById(Long id) { - return findByIdOptional(id) + Optional organization = findByIdOptional(id) .map(OrganizationEntityMapper::toDomain); + if (organization.isEmpty()) { + LOGGER.warnf("Organization %d was not found", id); + } + return organization; } -} \ No newline at end of file +} diff --git a/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/repository/ProjectRepositoryImpl.java b/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/repository/ProjectRepositoryImpl.java index fcbb823..a611e71 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/repository/ProjectRepositoryImpl.java +++ b/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/repository/ProjectRepositoryImpl.java @@ -7,6 +7,7 @@ import net.beetechgroup.beetask.entities.Project; import net.beetechgroup.beetask.frameworks.persistence.entities.ProjectEntity; import net.beetechgroup.beetask.frameworks.persistence.mapper.ProjectEntityMapper; import net.beetechgroup.beetask.usecase.repository.ProjectRepository; +import org.jboss.logging.Logger; import java.util.List; import java.util.Objects; @@ -14,6 +15,7 @@ import java.util.Optional; @ApplicationScoped public class ProjectRepositoryImpl implements ProjectRepository, PanacheRepository { + private static final Logger LOGGER = Logger.getLogger(ProjectRepositoryImpl.class); @Override @Transactional @@ -24,19 +26,27 @@ public class ProjectRepositoryImpl implements ProjectRepository, PanacheReposito } else { entity = getEntityManager().merge(entity); } - return ProjectEntityMapper.toDomain(entity); + Project savedProject = ProjectEntityMapper.toDomain(entity); + LOGGER.infof("Persisted project %d for organization %d", savedProject.getId(), savedProject.getOrganization().getId()); + return savedProject; } @Override public Optional findProjectById(Long id) { - return findByIdOptional(id) + Optional project = findByIdOptional(id) .map(ProjectEntityMapper::toDomain); + if (project.isEmpty()) { + LOGGER.warnf("Project %d was not found", id); + } + return project; } @Override public List findByOrganizationId(Long organizationId) { - return find("organization.id", organizationId).stream() + List projects = find("organization.id", organizationId).stream() .map(ProjectEntityMapper::toDomain) .toList(); + LOGGER.infof("Loaded %d projects for organization %d from database", projects.size(), organizationId); + return projects; } } diff --git a/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/repository/TaskRepositoryImpl.java b/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/repository/TaskRepositoryImpl.java index bcaf024..22fe044 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/repository/TaskRepositoryImpl.java +++ b/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/repository/TaskRepositoryImpl.java @@ -13,9 +13,11 @@ import net.beetechgroup.beetask.frameworks.persistence.entities.TaskEntity; import net.beetechgroup.beetask.frameworks.persistence.mapper.TaskEntityMapper; import net.beetechgroup.beetask.usecase.exceptions.TaskNotFoundException; import net.beetechgroup.beetask.usecase.repository.TaskRepository; +import org.jboss.logging.Logger; @ApplicationScoped public class TaskRepositoryImpl implements TaskRepository, PanacheRepository { + private static final Logger LOGGER = Logger.getLogger(TaskRepositoryImpl.class); @Override @Transactional @@ -26,19 +28,26 @@ public class TaskRepositoryImpl implements TaskRepository, PanacheRepository new TaskNotFoundException(id)); + .orElseThrow(() -> { + LOGGER.warnf("Task %d was not found in repository", id); + return new TaskNotFoundException(id); + }); return TaskEntityMapper.toDomain(entity); } @Override public List findAllTasks() { - return findAll().list().stream().map(TaskEntityMapper::toDomain).toList(); + List tasks = findAll().list().stream().map(TaskEntityMapper::toDomain).toList(); + LOGGER.infof("Loaded %d tasks from database", tasks.size()); + return tasks; } @Override @@ -61,7 +70,9 @@ public class TaskRepositoryImpl implements TaskRepository, PanacheRepository findTasksByUser(String email) { - return find("user.email = ?1", email).list().stream().map(TaskEntityMapper::toDomain).toList(); + List tasks = find("user.email = ?1", email).list().stream().map(TaskEntityMapper::toDomain).toList(); + LOGGER.infof("Loaded %d tasks for user %s", tasks.size(), email); + return tasks; } @Override @@ -100,7 +111,10 @@ public class TaskRepositoryImpl implements TaskRepository, PanacheRepository findTasksWorkedByUserEmailsInPeriod(List emails, LocalDateTime start, LocalDateTime end) { - if (emails.isEmpty()) return List.of(); + if (emails.isEmpty()) { + LOGGER.warn("Task search by user emails skipped because the email list is empty"); + return List.of(); + } return find("user.email in ?1", emails).list().stream() .filter(entity -> entity.getHistory().stream().anyMatch(h -> Objects.nonNull(h.getStartAt()) && @@ -112,7 +126,10 @@ public class TaskRepositoryImpl implements TaskRepository, PanacheRepository findTasksFinishedByUserEmailsInPeriod(List emails, LocalDateTime start, LocalDateTime end) { - if (emails.isEmpty()) return List.of(); + if (emails.isEmpty()) { + LOGGER.warn("Finished task search by user emails skipped because the email list is empty"); + return List.of(); + } return find("user.email in ?1 and status = ?2 and finishedAt >= ?3 and finishedAt <= ?4", emails, TaskStatus.COMPLETED, start, end) .list().stream().map(TaskEntityMapper::toDomain).toList(); diff --git a/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/repository/UserOrganizationRepositoryImpl.java b/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/repository/UserOrganizationRepositoryImpl.java index d83a94e..e2c7099 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/repository/UserOrganizationRepositoryImpl.java +++ b/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/repository/UserOrganizationRepositoryImpl.java @@ -8,6 +8,7 @@ import net.beetechgroup.beetask.entities.organization.UserOrganizationStatus; import net.beetechgroup.beetask.frameworks.persistence.entities.UserOrganizationEntity; import net.beetechgroup.beetask.frameworks.persistence.mapper.UserOrganizationEntityMapper; import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository; +import org.jboss.logging.Logger; import java.util.List; import java.util.Objects; @@ -15,33 +16,44 @@ import java.util.Optional; @ApplicationScoped public class UserOrganizationRepositoryImpl implements UserOrganizationRepository, PanacheRepository { + private static final Logger LOGGER = Logger.getLogger(UserOrganizationRepositoryImpl.class); @Override public List findByUserId(Long userId) { - return find("user.id", userId).stream() + List organizations = find("user.id", userId).stream() .map(UserOrganizationEntityMapper::toDomain) .toList(); + LOGGER.infof("Loaded %d organization memberships for user %d", organizations.size(), userId); + return organizations; } @Override public List findByUserIdAndStatus(Long userId, UserOrganizationStatus status) { - return find("user.id = ?1 and status = ?2", userId, status).stream() + List organizations = find("user.id = ?1 and status = ?2", userId, status).stream() .map(UserOrganizationEntityMapper::toDomain) .toList(); + LOGGER.infof("Loaded %d organization memberships for user %d with status %s", organizations.size(), userId, status); + return organizations; } @Override public List findByOrganizationIdAndStatus(Long organizationId, UserOrganizationStatus status) { - return find("organization.id = ?1 and status = ?2", organizationId, status).stream() + List organizations = find("organization.id = ?1 and status = ?2", organizationId, status).stream() .map(UserOrganizationEntityMapper::toDomain) .toList(); + LOGGER.infof("Loaded %d memberships for organization %d with status %s", organizations.size(), organizationId, status); + return organizations; } @Override public Optional findByUserAndOrganization(Long userId, Long organizationId) { - return find("user.id = ?1 and organization.id = ?2", userId, organizationId) + Optional association = find("user.id = ?1 and organization.id = ?2", userId, organizationId) .firstResultOptional() .map(UserOrganizationEntityMapper::toDomain); + if (association.isEmpty()) { + LOGGER.warnf("Membership for user %d and organization %d was not found", userId, organizationId); + } + return association; } @Override @@ -56,9 +68,13 @@ public class UserOrganizationRepositoryImpl implements UserOrganizationRepositor existingEntity.setRole(entity.getRole()); existingEntity.setStatus(entity.getStatus()); getEntityManager().merge(existingEntity); + LOGGER.infof("Updated membership for user %d in organization %d to role %s and status %s", + entity.getUser().getId(), entity.getOrganization().getId(), entity.getRole(), entity.getStatus()); return; } } persist(entity); + LOGGER.infof("Created membership for user %d in organization %d with role %s and status %s", + entity.getUser().getId(), entity.getOrganization().getId(), entity.getRole(), entity.getStatus()); } } diff --git a/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/repository/UserRepositoryImpl.java b/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/repository/UserRepositoryImpl.java index 4410295..cdfe325 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/repository/UserRepositoryImpl.java +++ b/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/repository/UserRepositoryImpl.java @@ -12,12 +12,14 @@ import net.beetechgroup.beetask.entities.organization.UserOrganization; import net.beetechgroup.beetask.entities.organization.UserOrganizationStatus; import net.beetechgroup.beetask.usecase.repository.UserRepository; import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository; +import org.jboss.logging.Logger; import java.util.List; import java.util.Optional; @ApplicationScoped public class UserRepositoryImpl implements UserRepository, PanacheRepository { + private static final Logger LOGGER = Logger.getLogger(UserRepositoryImpl.class); @Inject UserOrganizationRepository userOrganizationRepository; @@ -31,23 +33,35 @@ public class UserRepositoryImpl implements UserRepository, PanacheRepository findByEmail(String email) { - return find("email", email).firstResultOptional() + Optional user = find("email", email).firstResultOptional() .map(UserEntityMapper::toDomain); + if (user.isEmpty()) { + LOGGER.warnf("User with email %s was not found", email); + } + return user; } @Override public Optional findUserById(Long id) { - return findByIdOptional(id) + Optional user = findByIdOptional(id) .map(UserEntityMapper::toDomain); + if (user.isEmpty()) { + LOGGER.warnf("User with id %d was not found", id); + } + return user; } @Override public List findUserOrganizations(Long userId) { - return userOrganizationRepository.findByUserIdAndStatus(userId, UserOrganizationStatus.ACTIVE); + List organizations = userOrganizationRepository.findByUserIdAndStatus(userId, UserOrganizationStatus.ACTIVE); + LOGGER.infof("Loaded %d active organizations for user %d", organizations.size(), userId); + return organizations; } } diff --git a/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/auth/AuthController.java b/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/auth/AuthController.java index ad18f42..e37bb9c 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/auth/AuthController.java +++ b/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/auth/AuthController.java @@ -27,12 +27,14 @@ import io.quarkus.security.identity.SecurityIdentity; import jakarta.ws.rs.GET; import net.beetechgroup.beetask.usecase.user.profile.GetUserProfileUseCase; import net.beetechgroup.beetask.usecase.user.profile.UserProfileOutput; +import org.jboss.logging.Logger; @Path("/auth") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @Tag(name = "Authentication", description = "Authentication operations") public class AuthController { + private static final Logger LOGGER = Logger.getLogger(AuthController.class); @Inject CreateUserUseCase createUserUseCase; @@ -55,6 +57,7 @@ public class AuthController { @Operation(summary = "Get current user profile", description = "Returns details of the logged in user") public UserProfileOutput me() { String email = securityIdentity.getPrincipal().getName(); + LOGGER.infof("Profile requested for authenticated user %s", email); return getUserProfileUseCase.execute(email); } @@ -62,8 +65,10 @@ public class AuthController { @POST @Operation(summary = "Register", description = "Creates a new user account") public CreateAccountResponse register(CreateAccountRequest request) { + LOGGER.infof("Registration requested for email %s", request.email()); CreateUserInput input = AuthControllerMapper.toCreateUserInput(request); CreateUserOutput output = createUserUseCase.execute(input); + LOGGER.infof("User account created successfully for email %s", output.email()); return AuthControllerMapper.toCreateAccountResponse(output); } @@ -71,8 +76,10 @@ public class AuthController { @POST @Operation(summary = "Login", description = "Login that returns real user data and JWT") public LoginResponse login(LoginRequest request) { + LOGGER.infof("Login requested for email %s", request.email()); LoginInput input = AuthControllerMapper.toLoginInput(request); LoginOutput output = loginUseCase.execute(input); + LOGGER.infof("Login succeeded for email %s", output.email()); return AuthControllerMapper.toLoginResponse(output); } @@ -80,8 +87,10 @@ public class AuthController { @POST @Operation(summary = "Refresh token", description = "Token refresh using refresh token") public LoginResponse refresh(RefreshTokenRequest request) { + LOGGER.info("Refresh token requested"); RefreshTokenInput input = AuthControllerMapper.toRefreshTokenInput(request); LoginOutput output = refreshTokenUseCase.execute(input); + LOGGER.infof("Refresh token succeeded for email %s", output.email()); return AuthControllerMapper.toLoginResponse(output); } } diff --git a/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/dashboard/DashboardController.java b/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/dashboard/DashboardController.java index def3872..99785be 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/dashboard/DashboardController.java +++ b/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/dashboard/DashboardController.java @@ -11,6 +11,7 @@ import jakarta.ws.rs.core.MediaType; import net.beetechgroup.beetask.usecase.dashboard.DashboardInput; import net.beetechgroup.beetask.usecase.dashboard.DashboardOutput; import net.beetechgroup.beetask.usecase.dashboard.DashboardUseCase; +import org.jboss.logging.Logger; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -19,6 +20,7 @@ import java.time.format.DateTimeFormatter; @Produces(MediaType.APPLICATION_JSON) @Authenticated public class DashboardController { + private static final Logger LOGGER = Logger.getLogger(DashboardController.class); @Inject DashboardUseCase dashboardUseCase; @@ -46,6 +48,7 @@ public class DashboardController { } DashboardInput input = new DashboardInput(email, start, end); + LOGGER.infof("Dashboard requested by user %s for period %s to %s", email, start, end); return dashboardUseCase.execute(input); } } diff --git a/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/organization/OrganizationController.java b/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/organization/OrganizationController.java index 345d315..ad2700e 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/organization/OrganizationController.java +++ b/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/organization/OrganizationController.java @@ -21,11 +21,13 @@ import net.beetechgroup.beetask.usecase.organization.requests.ListUserJoinReques import net.beetechgroup.beetask.usecase.organization.requests.UserJoinRequestOutput; import net.beetechgroup.beetask.usecase.organization.requests.JoinRequestOutput; import net.beetechgroup.beetask.usecase.organization.requests.HandleJoinRequestUseCase; +import org.jboss.logging.Logger; import java.util.List; @Path("/organizations") public class OrganizationController { + private static final Logger LOGGER = Logger.getLogger(OrganizationController.class); @Inject CreateOrganizationUseCase createOrganizationUseCase; @@ -53,8 +55,10 @@ public class OrganizationController { @Operation(summary = "Create organization", description = "Creates a new organization") public CreateOrganizationResponse createOrganization(CreateOrganizationRequest request) { String userEmail = securityIdentity.getPrincipal().getName(); + LOGGER.infof("User %s requested organization creation with name '%s'", userEmail, request.name()); CreateOrganizationInput input = OrganizationControllerMapper.toCreateOrganizationInput(request, userEmail); CreateOrganizationOutput output = createOrganizationUseCase.execute(input); + LOGGER.infof("Organization %d created successfully by user %s", output.id(), userEmail); return OrganizationControllerMapper.toCreateOrganizationResponse(output); } @@ -63,7 +67,9 @@ public class OrganizationController { @Authenticated @Operation(summary = "Search organizations", description = "Search for organizations by name") public List search(@QueryParam("q") String query) { - return searchOrganizationsUseCase.execute(query); + List organizations = searchOrganizationsUseCase.execute(query); + LOGGER.infof("Organization search executed with query '%s' and returned %d results", query, organizations.size()); + return organizations; } @POST @@ -72,7 +78,9 @@ public class OrganizationController { @Operation(summary = "Request to join", description = "Request to join an organization") public void requestJoin(@PathParam("id") Long organizationId) { String userEmail = securityIdentity.getPrincipal().getName(); + LOGGER.infof("User %s requested to join organization %d", userEmail, organizationId); requestJoinOrganizationUseCase.execute(organizationId, userEmail); + LOGGER.infof("Join request created for user %s in organization %d", userEmail, organizationId); } @GET @@ -81,7 +89,9 @@ public class OrganizationController { @Operation(summary = "List pending requests", description = "List all pending join requests for an organization (Owner only)") public List listRequests(@PathParam("id") Long organizationId) { String userEmail = securityIdentity.getPrincipal().getName(); - return listPendingRequestsUseCase.execute(userEmail, organizationId, null); + List requests = listPendingRequestsUseCase.execute(userEmail, organizationId, null); + LOGGER.infof("User %s listed %d pending requests for organization %d", userEmail, requests.size(), organizationId); + return requests; } @PATCH @@ -90,7 +100,9 @@ public class OrganizationController { @Operation(summary = "Approve request", description = "Approve a join request (Owner only)") public void approveRequest(@PathParam("id") Long organizationId, @PathParam("userId") Long userId) { String userEmail = securityIdentity.getPrincipal().getName(); + LOGGER.infof("User %s requested approval for user %d in organization %d", userEmail, userId, organizationId); handleJoinRequestUseCase.execute(userEmail, organizationId, new HandleJoinRequestUseCase.Input(userId, true)); + LOGGER.infof("Join request approved for user %d in organization %d by %s", userId, organizationId, userEmail); } @PATCH @@ -99,7 +111,9 @@ public class OrganizationController { @Operation(summary = "Reject request", description = "Reject a join request (Owner only)") public void rejectRequest(@PathParam("id") Long organizationId, @PathParam("userId") Long userId) { String userEmail = securityIdentity.getPrincipal().getName(); + LOGGER.infof("User %s requested rejection for user %d in organization %d", userEmail, userId, organizationId); handleJoinRequestUseCase.execute(userEmail, organizationId, new HandleJoinRequestUseCase.Input(userId, false)); + LOGGER.infof("Join request rejected for user %d in organization %d by %s", userId, organizationId, userEmail); } @GET @@ -108,6 +122,8 @@ public class OrganizationController { @Operation(summary = "List my requests", description = "List all join requests made by the current user") public List listMyRequests() { String userEmail = securityIdentity.getPrincipal().getName(); - return listUserJoinRequestsUseCase.execute(userEmail); + List requests = listUserJoinRequestsUseCase.execute(userEmail); + LOGGER.infof("User %s listed %d organization join requests", userEmail, requests.size()); + return requests; } } diff --git a/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/orgdashboard/MemberDetailController.java b/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/orgdashboard/MemberDetailController.java index 50a5a0a..3e1e40d 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/orgdashboard/MemberDetailController.java +++ b/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/orgdashboard/MemberDetailController.java @@ -11,6 +11,7 @@ import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; import net.beetechgroup.beetask.usecase.orgdashboard.memberdetail.MemberDetailInput; import net.beetechgroup.beetask.usecase.orgdashboard.memberdetail.MemberDetailUseCase; +import org.jboss.logging.Logger; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -19,6 +20,7 @@ import java.time.format.DateTimeFormatter; @Produces(MediaType.APPLICATION_JSON) @Authenticated public class MemberDetailController { + private static final Logger LOGGER = Logger.getLogger(MemberDetailController.class); @Inject MemberDetailUseCase memberDetailUseCase; @@ -47,6 +49,8 @@ public class MemberDetailController { end = end.with(java.time.LocalTime.MAX); } + LOGGER.infof("Member stats requested by user %s for organization %d member %d and period %s to %s", + email, organizationId, memberId, start, end); return MemberDetailControllerMapper.toResponse( memberDetailUseCase.execute(new MemberDetailInput(email, organizationId, memberId, start, end)) ); diff --git a/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/orgdashboard/OrgDashboardController.java b/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/orgdashboard/OrgDashboardController.java index 5b37fdb..80ec9e3 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/orgdashboard/OrgDashboardController.java +++ b/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/orgdashboard/OrgDashboardController.java @@ -11,6 +11,7 @@ import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; import net.beetechgroup.beetask.usecase.orgdashboard.OrgDashboardInput; import net.beetechgroup.beetask.usecase.orgdashboard.OrgDashboardUseCase; +import org.jboss.logging.Logger; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -19,6 +20,7 @@ import java.time.format.DateTimeFormatter; @Produces(MediaType.APPLICATION_JSON) @Authenticated public class OrgDashboardController { + private static final Logger LOGGER = Logger.getLogger(OrgDashboardController.class); @Inject OrgDashboardUseCase orgDashboardUseCase; @@ -46,6 +48,8 @@ public class OrgDashboardController { end = end.with(java.time.LocalTime.MAX); } + LOGGER.infof("Organization dashboard requested by user %s for organization %d and period %s to %s", + email, organizationId, start, end); return OrgDashboardControllerMapper.toResponse( orgDashboardUseCase.execute(new OrgDashboardInput(email, organizationId, start, end)) ); diff --git a/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/project/ProjectController.java b/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/project/ProjectController.java index a153bd5..fa9e5b3 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/project/ProjectController.java +++ b/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/project/ProjectController.java @@ -12,6 +12,7 @@ import jakarta.ws.rs.core.MediaType; import net.beetechgroup.beetask.entities.Project; import net.beetechgroup.beetask.usecase.project.create.CreateProjectUseCase; import net.beetechgroup.beetask.usecase.project.list.ListProjectsUseCase; +import org.jboss.logging.Logger; import java.util.List; @@ -19,6 +20,7 @@ import java.util.List; @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class ProjectController { + private static final Logger LOGGER = Logger.getLogger(ProjectController.class); @Inject CreateProjectUseCase createProjectUseCase; @@ -29,15 +31,19 @@ public class ProjectController { @GET @Authenticated public List listProjects(@PathParam("orgId") Long orgId) { - return listProjectsUseCase.execute(orgId).stream() + List projects = listProjectsUseCase.execute(orgId).stream() .map(p -> new ProjectResponse(p.getId(), p.getName())) .toList(); + LOGGER.infof("Listed %d projects for organization %d", projects.size(), orgId); + return projects; } @POST @Authenticated public ProjectResponse createProject(@PathParam("orgId") Long orgId, CreateProjectRequest request) { + LOGGER.infof("Project creation requested for organization %d with name '%s'", orgId, request.name()); Project project = createProjectUseCase.execute(orgId, request.name()); + LOGGER.infof("Project %d created successfully for organization %d", project.getId(), orgId); return new ProjectResponse(project.getId(), project.getName()); } diff --git a/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/task/TaskController.java b/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/task/TaskController.java index 1e95788..8de8895 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/task/TaskController.java +++ b/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/task/TaskController.java @@ -18,6 +18,7 @@ import java.util.List; import org.eclipse.microprofile.openapi.annotations.Operation; import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; import org.eclipse.microprofile.openapi.annotations.tags.Tag; +import org.jboss.logging.Logger; import net.beetechgroup.beetask.entities.task.TaskStatus; import net.beetechgroup.beetask.usecase.task.create.CreateTaskInput; @@ -36,6 +37,7 @@ import net.beetechgroup.beetask.usecase.task.get.GetTaskUseCase; @Consumes(MediaType.APPLICATION_JSON) @Tag(name = "Tasks", description = "Task management operations") public class TaskController { + private static final Logger LOGGER = Logger.getLogger(TaskController.class); @Inject CreateTaskUseCase createTaskUseCase; @@ -69,8 +71,10 @@ public class TaskController { @APIResponse(responseCode = "201", description = "Task created successfully") public CreateTaskResponse createTask(CreateTaskRequest request) { String email = securityIdentity.getPrincipal().getName(); + LOGGER.infof("User %s requested task creation with title '%s'", email, request.title()); CreateTaskInput input = TaskControllerMapper.toCreateTaskInput(request, email); CreateTaskOutput output = createTaskUseCase.execute(input); + LOGGER.infof("Task %d created successfully by user %s", output.id(), email); return TaskControllerMapper.toCreateTaskResponse(output); } @@ -80,7 +84,9 @@ public class TaskController { @APIResponse(responseCode = "200", description = "Task updated successfully") public CreateTaskResponse updateTask(@PathParam("id") Long id, CreateTaskRequest request) { String email = securityIdentity.getPrincipal().getName(); + LOGGER.infof("User %s requested update for task %d", email, id); CreateTaskOutput output = updateTaskUseCase.execute(TaskControllerMapper.toUpdateTaskInput(id, request, email)); + LOGGER.infof("Task %d updated successfully by user %s", id, email); return TaskControllerMapper.toCreateTaskResponse(output); } @@ -89,6 +95,7 @@ public class TaskController { @Operation(summary = "Get a task", description = "Gets a specific task by ID") @APIResponse(responseCode = "200", description = "Task retrieved successfully") public CreateTaskResponse getTask(@PathParam("id") Long id) { + LOGGER.infof("Task details requested for task %d", id); return TaskControllerMapper.toCreateTaskResponse(getTaskUseCase.execute(id)); } @@ -98,7 +105,9 @@ public class TaskController { @APIResponse(responseCode = "200", description = "Task started successfully") public CreateTaskResponse startTask(@PathParam("id") Long id) { String email = securityIdentity.getPrincipal().getName(); + LOGGER.infof("User %s requested start for task %d", email, id); CreateTaskOutput output = startTaskUseCase.execute(new StartTaskInput(id, email)); + LOGGER.infof("Task %d started successfully by user %s", id, email); return TaskControllerMapper.toCreateTaskResponse(output); } @@ -107,7 +116,9 @@ public class TaskController { @Operation(summary = "Stop a task", description = "Stops a task in the system") @APIResponse(responseCode = "200", description = "Task stopped successfully") public CreateTaskResponse stopTask(@PathParam("id") Long id) { + LOGGER.infof("Stop requested for task %d", id); CreateTaskOutput output = stopTaskUseCase.execute(TaskControllerMapper.toStopTaskInput(id)); + LOGGER.infof("Task %d stopped successfully", id); return TaskControllerMapper.toCreateTaskResponse(output); } @@ -116,7 +127,9 @@ public class TaskController { @Operation(summary = "Update task status", description = "Updates the status of a task") @APIResponse(responseCode = "200", description = "Task status updated successfully") public CreateTaskResponse updateTaskStatus(@PathParam("id") Long id, TaskStatus status) { + LOGGER.infof("Status update requested for task %d to %s", id, status); CreateTaskOutput output = updateTaskStatusUseCase.execute(id, status); + LOGGER.infof("Task %d status updated successfully to %s", id, status); return TaskControllerMapper.toCreateTaskResponse(output); } @@ -124,7 +137,9 @@ public class TaskController { @Operation(summary = "List all tasks", description = "Lists all tasks in the system") @APIResponse(responseCode = "200", description = "Tasks listed successfully") public List listTasks() { - return listAllTasksUseCase.execute().stream().map(TaskControllerMapper::toCreateTaskResponse).toList(); + List tasks = listAllTasksUseCase.execute().stream().map(TaskControllerMapper::toCreateTaskResponse).toList(); + LOGGER.infof("Listed %d tasks", tasks.size()); + return tasks; } @GET @@ -133,7 +148,9 @@ public class TaskController { @APIResponse(responseCode = "200", description = "Tasks listed successfully") public List listMyTasks() { String email = securityIdentity.getPrincipal().getName(); - return listMyTasksUseCase.execute(email).stream().map(TaskControllerMapper::toCreateTaskResponse).toList(); + List tasks = listMyTasksUseCase.execute(email).stream().map(TaskControllerMapper::toCreateTaskResponse).toList(); + LOGGER.infof("Listed %d tasks for user %s", tasks.size(), email); + return tasks; } } diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/auth/login/LoginUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/auth/login/LoginUseCase.java index 09c8951..c242390 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/auth/login/LoginUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/auth/login/LoginUseCase.java @@ -12,8 +12,10 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import org.jboss.logging.Logger; public class LoginUseCase { + private static final Logger LOGGER = Logger.getLogger(LoginUseCase.class); private final UserRepository userRepository; private final String issuer; @@ -27,6 +29,7 @@ public class LoginUseCase { Optional userOptional = userRepository.findByEmail(input.email()); if (userOptional.isEmpty()) { + LOGGER.warnf("Login failed because email %s was not found", input.email()); throw new RuntimeException("Invalid credentials"); } @@ -34,9 +37,11 @@ public class LoginUseCase { // Simple password check for now (should use Bcrypt) if (!user.getPassword().equals(input.password())) { + LOGGER.warnf("Login failed because password did not match for email %s", input.email()); throw new RuntimeException("Invalid credentials"); } + LOGGER.infof("Login authenticated for user %s", input.email()); List userOrganizations = userRepository.findUserOrganizations(user.getId()); Set groups = new HashSet<>(); @@ -67,6 +72,7 @@ public class LoginUseCase { .expiresIn(86400) // 24 hours .sign(); + LOGGER.infof("Login completed for user %s with %d organizations", user.getEmail(), orgs.size()); return new LoginOutput( user.getName(), user.getEmail(), diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/auth/refresh/RefreshTokenUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/auth/refresh/RefreshTokenUseCase.java index ccb874a..8c6c2ed 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/auth/refresh/RefreshTokenUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/auth/refresh/RefreshTokenUseCase.java @@ -16,8 +16,10 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import org.jboss.logging.Logger; public class RefreshTokenUseCase { + private static final Logger LOGGER = Logger.getLogger(RefreshTokenUseCase.class); private final UserRepository userRepository; private final JWTParser jwtParser; @@ -35,11 +37,14 @@ public class RefreshTokenUseCase { String email = token.getSubject(); // Or token.getName() / token.getClaim(Claims.upn.name()) if (Objects.isNull(email)) { + LOGGER.warn("Refresh token rejected because subject/email was not present"); throw new RuntimeException("Invalid refresh token: email not found"); } + LOGGER.infof("Refreshing authentication token for user %s", email); Optional userOptional = userRepository.findByEmail(email); if (userOptional.isEmpty()) { + LOGGER.warnf("Refresh token rejected because user %s was not found", email); throw new RuntimeException("User not found for the provided refresh token"); } @@ -75,6 +80,7 @@ public class RefreshTokenUseCase { }) .toList(); + LOGGER.infof("Refresh token completed for user %s with %d organizations", user.getEmail(), orgs.size()); return new LoginOutput( user.getName(), user.getEmail(), @@ -86,6 +92,7 @@ public class RefreshTokenUseCase { orgs ); } catch (ParseException e) { + LOGGER.error("Refresh token parsing failed", e); throw new RuntimeException("Invalid refresh token: " + e.getMessage()); } } diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/dashboard/DashboardUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/dashboard/DashboardUseCase.java index f7ea128..f1a0f5b 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/dashboard/DashboardUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/dashboard/DashboardUseCase.java @@ -13,8 +13,10 @@ import net.beetechgroup.beetask.entities.task.Task; import net.beetechgroup.beetask.entities.task.TaskHistoryItem; import net.beetechgroup.beetask.usecase.repository.TaskRepository; import net.beetechgroup.beetask.usecase.task.create.CreateTaskMapper; +import org.jboss.logging.Logger; public class DashboardUseCase { + private static final Logger LOGGER = Logger.getLogger(DashboardUseCase.class); private final TaskRepository taskRepository; @@ -23,6 +25,8 @@ public class DashboardUseCase { } public DashboardOutput execute(DashboardInput input) { + LOGGER.infof("Calculating dashboard for user %s in period %s to %s", + input.userEmail(), input.startDate(), input.endDate()); List workedTasks = taskRepository.findTasksWorkedByUserInPeriod( input.userEmail(), input.startDate(), input.endDate() ); @@ -61,12 +65,15 @@ public class DashboardUseCase { } } - return new DashboardOutput( + DashboardOutput output = new DashboardOutput( totalMinutes, new ArrayList<>(projectStatsMap.values()), yesterdayTasks.stream().map(CreateTaskMapper::toCreateTaskOutput).toList(), finishedTasks.stream().map(CreateTaskMapper::toCreateTaskOutput).toList() ); + LOGGER.infof("Dashboard calculated for user %s with %d worked tasks, %d finished tasks and %d total minutes", + input.userEmail(), workedTasks.size(), finishedTasks.size(), totalMinutes); + return output; } private boolean isWithinPeriod(TaskHistoryItem item, LocalDateTime start, LocalDateTime end) { diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/auth/AuthorizeOrganizationAdminUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/auth/AuthorizeOrganizationAdminUseCase.java index 8f89f4c..912b972 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/auth/AuthorizeOrganizationAdminUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/auth/AuthorizeOrganizationAdminUseCase.java @@ -7,8 +7,10 @@ import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository; import net.beetechgroup.beetask.usecase.repository.UserRepository; import java.util.Optional; +import org.jboss.logging.Logger; public class AuthorizeOrganizationAdminUseCase { + private static final Logger LOGGER = Logger.getLogger(AuthorizeOrganizationAdminUseCase.class); private final UserRepository userRepository; private final UserOrganizationRepository userOrganizationRepository; @@ -21,6 +23,7 @@ public class AuthorizeOrganizationAdminUseCase { public void execute(String userEmail, Long organizationId) { Optional userOptional = userRepository.findByEmail(userEmail); if (userOptional.isEmpty()) { + LOGGER.warnf("Organization admin authorization failed because user %s was not found", userEmail); throw new RuntimeException("Unauthorized: User not found"); } @@ -28,12 +31,17 @@ public class AuthorizeOrganizationAdminUseCase { Optional association = userOrganizationRepository.findByUserAndOrganization(user.getId(), organizationId); if (association.isEmpty()) { + LOGGER.warnf("Organization admin authorization failed because user %s is not a member of organization %d", + userEmail, organizationId); throw new RuntimeException("Forbidden: Not a member of this organization"); } UserOrganization uo = association.get(); if (uo.getRole() != UserOrganizationRole.OWNER && uo.getRole() != UserOrganizationRole.ADMIN) { + LOGGER.warnf("Organization admin authorization failed because user %s has role %s in organization %d", + userEmail, uo.getRole(), organizationId); throw new RuntimeException("Forbidden: Only owners or admins can perform this action"); } + LOGGER.infof("Organization admin authorization granted to user %s for organization %d", userEmail, organizationId); } } diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/create/CreateOrganizationUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/create/CreateOrganizationUseCase.java index 21dec34..733f275 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/create/CreateOrganizationUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/create/CreateOrganizationUseCase.java @@ -10,8 +10,10 @@ import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository; import net.beetechgroup.beetask.usecase.repository.UserRepository; import java.util.Optional; +import org.jboss.logging.Logger; public class CreateOrganizationUseCase { + private static final Logger LOGGER = Logger.getLogger(CreateOrganizationUseCase.class); private final OrganizationRepository organizationRepository; private final UserRepository userRepository; private final UserOrganizationRepository userOrganizationRepository; @@ -27,10 +29,12 @@ public class CreateOrganizationUseCase { public CreateOrganizationOutput execute(CreateOrganizationInput input) { Optional userOptional = userRepository.findByEmail(input.userEmail()); if (userOptional.isEmpty()) { + LOGGER.warnf("Organization creation failed because user %s was not found", input.userEmail()); throw new RuntimeException("User not found: " + input.userEmail()); } User user = userOptional.get(); + LOGGER.infof("Creating organization '%s' for owner %s", input.name(), input.userEmail()); Organization organization = new Organization(); organization.setName(input.name()); organization = organizationRepository.saveOrganization(organization); @@ -41,6 +45,7 @@ public class CreateOrganizationUseCase { userOrganization.setRole(UserOrganizationRole.OWNER); userOrganization.setStatus(UserOrganizationStatus.ACTIVE); userOrganizationRepository.save(userOrganization); + LOGGER.infof("Organization %d created successfully with owner user %d", organization.getId(), user.getId()); return CreateOrganizationMapper.toCreateOrganizationOutput(organization); } diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/join/RequestJoinOrganizationUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/join/RequestJoinOrganizationUseCase.java index 34c8c57..94aa749 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/join/RequestJoinOrganizationUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/join/RequestJoinOrganizationUseCase.java @@ -10,8 +10,10 @@ import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository; import net.beetechgroup.beetask.usecase.repository.UserRepository; import java.util.Optional; +import org.jboss.logging.Logger; public class RequestJoinOrganizationUseCase { + private static final Logger LOGGER = Logger.getLogger(RequestJoinOrganizationUseCase.class); private final OrganizationRepository organizationRepository; private final UserRepository userRepository; private final UserOrganizationRepository userOrganizationRepository; @@ -29,6 +31,7 @@ public class RequestJoinOrganizationUseCase { Optional orgOptional = organizationRepository.findOrganizationById(organizationId); if (userOptional.isEmpty() || orgOptional.isEmpty()) { + LOGGER.warnf("Join request failed because user %s or organization %d was not found", userEmail, organizationId); throw new RuntimeException("User or Organization not found"); } @@ -38,8 +41,10 @@ public class RequestJoinOrganizationUseCase { Optional existing = userOrganizationRepository.findByUserAndOrganization(user.getId(), organization.getId()); if (existing.isPresent()) { if (existing.get().getStatus() == UserOrganizationStatus.ACTIVE) { + LOGGER.warnf("Join request ignored because user %s is already active in organization %d", userEmail, organizationId); throw new RuntimeException("User is already a member of this organization"); } else if (existing.get().getStatus() == UserOrganizationStatus.PENDING) { + LOGGER.warnf("Join request ignored because user %s already has a pending request for organization %d", userEmail, organizationId); throw new RuntimeException("A join request is already pending for this organization"); } // If REJECTED, we allow a new request (or we could just update the existing one to PENDING) @@ -52,5 +57,6 @@ public class RequestJoinOrganizationUseCase { request.setStatus(UserOrganizationStatus.PENDING); userOrganizationRepository.save(request); + LOGGER.infof("Join request created for user %s in organization %d", userEmail, organizationId); } } diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/requests/HandleJoinRequestUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/requests/HandleJoinRequestUseCase.java index 8e6198d..7ae8bc5 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/requests/HandleJoinRequestUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/requests/HandleJoinRequestUseCase.java @@ -7,8 +7,10 @@ import net.beetechgroup.beetask.usecase.organization.auth.AuthorizeOrganizationA import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository; import java.util.Optional; +import org.jboss.logging.Logger; public class HandleJoinRequestUseCase extends AuthenticatedOrganizationUseCase { + private static final Logger LOGGER = Logger.getLogger(HandleJoinRequestUseCase.class); private final UserOrganizationRepository userOrganizationRepository; public HandleJoinRequestUseCase(AuthorizeOrganizationAdminUseCase authorizer, @@ -22,16 +24,22 @@ public class HandleJoinRequestUseCase extends AuthenticatedOrganizationUseCase associationOptional = userOrganizationRepository.findByUserAndOrganization(input.userId(), organizationId); if (associationOptional.isEmpty()) { + LOGGER.warnf("Join request handling failed because request for user %d in organization %d was not found", + input.userId(), organizationId); throw new RuntimeException("Request not found"); } UserOrganization association = associationOptional.get(); if (association.getStatus() != UserOrganizationStatus.PENDING) { + LOGGER.warnf("Join request handling failed because request for user %d in organization %d is %s", + input.userId(), organizationId, association.getStatus()); throw new RuntimeException("Request is not pending"); } association.setStatus(input.approved() ? UserOrganizationStatus.ACTIVE : UserOrganizationStatus.REJECTED); userOrganizationRepository.save(association); + LOGGER.infof("Join request for user %d in organization %d was %s", + input.userId(), organizationId, input.approved() ? "approved" : "rejected"); return null; } diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/requests/ListPendingRequestsUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/requests/ListPendingRequestsUseCase.java index 10b692d..9179657 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/requests/ListPendingRequestsUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/requests/ListPendingRequestsUseCase.java @@ -8,8 +8,10 @@ import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository; import java.util.List; import java.util.stream.Collectors; +import org.jboss.logging.Logger; public class ListPendingRequestsUseCase extends AuthenticatedOrganizationUseCase> { + private static final Logger LOGGER = Logger.getLogger(ListPendingRequestsUseCase.class); private final UserOrganizationRepository userOrganizationRepository; public ListPendingRequestsUseCase(AuthorizeOrganizationAdminUseCase authorizer, @@ -21,7 +23,7 @@ public class ListPendingRequestsUseCase extends AuthenticatedOrganizationUseCase @Override protected List doExecute(Long organizationId, Void input) { List pending = userOrganizationRepository.findByOrganizationIdAndStatus(organizationId, UserOrganizationStatus.PENDING); - return pending.stream() + List requests = pending.stream() .map(uo -> new JoinRequestOutput( uo.getUser().getId(), uo.getUser().getName(), @@ -29,5 +31,7 @@ public class ListPendingRequestsUseCase extends AuthenticatedOrganizationUseCase uo.getUser().getPhoto() )) .collect(Collectors.toList()); + LOGGER.infof("Loaded %d pending requests for organization %d", requests.size(), organizationId); + return requests; } } diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/requests/ListUserJoinRequestsUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/requests/ListUserJoinRequestsUseCase.java index 65b76ad..15f0dd9 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/requests/ListUserJoinRequestsUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/requests/ListUserJoinRequestsUseCase.java @@ -5,8 +5,10 @@ import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository; import net.beetechgroup.beetask.usecase.repository.UserRepository; import java.util.List; +import org.jboss.logging.Logger; public class ListUserJoinRequestsUseCase { + private static final Logger LOGGER = Logger.getLogger(ListUserJoinRequestsUseCase.class); private final UserOrganizationRepository userOrganizationRepository; private final UserRepository userRepository; @@ -17,14 +19,19 @@ public class ListUserJoinRequestsUseCase { public List execute(String userEmail) { User user = userRepository.findByEmail(userEmail) - .orElseThrow(() -> new RuntimeException("User not found")); + .orElseThrow(() -> { + LOGGER.warnf("Join request listing failed because user %s was not found", userEmail); + return new RuntimeException("User not found"); + }); - return userOrganizationRepository.findByUserId(user.getId()).stream() + List requests = userOrganizationRepository.findByUserId(user.getId()).stream() .map(uo -> new UserJoinRequestOutput( uo.getOrganization().getId(), uo.getOrganization().getName(), uo.getStatus() )) .toList(); + LOGGER.infof("Loaded %d join requests for user %s", requests.size(), userEmail); + return requests; } } diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/search/SearchOrganizationsUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/search/SearchOrganizationsUseCase.java index 8ff870e..7f0a57d 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/search/SearchOrganizationsUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/organization/search/SearchOrganizationsUseCase.java @@ -3,8 +3,10 @@ package net.beetechgroup.beetask.usecase.organization.search; import net.beetechgroup.beetask.usecase.repository.OrganizationRepository; import java.util.List; +import org.jboss.logging.Logger; public class SearchOrganizationsUseCase { + private static final Logger LOGGER = Logger.getLogger(SearchOrganizationsUseCase.class); private final OrganizationRepository organizationRepository; public SearchOrganizationsUseCase(OrganizationRepository organizationRepository) { @@ -12,8 +14,10 @@ public class SearchOrganizationsUseCase { } public List execute(String query) { - return organizationRepository.search(query).stream() + List organizations = organizationRepository.search(query).stream() .map(org -> new SearchOrganizationOutput(org.getId(), org.getName())) .toList(); + LOGGER.infof("Organization search for query '%s' returned %d results", query, organizations.size()); + return organizations; } } diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/orgdashboard/OrgDashboardUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/orgdashboard/OrgDashboardUseCase.java index 5febf56..c5796b8 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/orgdashboard/OrgDashboardUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/orgdashboard/OrgDashboardUseCase.java @@ -16,8 +16,10 @@ import net.beetechgroup.beetask.entities.task.TaskHistoryItem; import net.beetechgroup.beetask.usecase.organization.auth.AuthorizeOrganizationAdminUseCase; import net.beetechgroup.beetask.usecase.repository.TaskRepository; import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository; +import org.jboss.logging.Logger; public class OrgDashboardUseCase { + private static final Logger LOGGER = Logger.getLogger(OrgDashboardUseCase.class); private final TaskRepository taskRepository; private final UserOrganizationRepository userOrganizationRepository; @@ -32,6 +34,8 @@ public class OrgDashboardUseCase { } public OrgDashboardOutput execute(OrgDashboardInput input) { + LOGGER.infof("Calculating organization dashboard for org %d requested by %s in period %s to %s", + input.organizationId(), input.userEmail(), input.startDate(), input.endDate()); authorizer.execute(input.userEmail(), input.organizationId()); List members = userOrganizationRepository.findByOrganizationIdAndStatus( @@ -98,11 +102,14 @@ public class OrgDashboardUseCase { .sorted(Comparator.comparingLong(OrgMemberStats::totalMinutesWorked).reversed()) .toList(); - return new OrgDashboardOutput( + OrgDashboardOutput output = new OrgDashboardOutput( new ArrayList<>(projectStatsMap.values()), topTasks, memberStats ); + LOGGER.infof("Organization dashboard calculated for org %d with %d members, %d worked tasks and %d finished tasks", + input.organizationId(), members.size(), workedTasks.size(), finishedTasks.size()); + return output; } private long calculateMinutesInPeriod(Task task, LocalDateTime start, LocalDateTime end) { diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/orgdashboard/memberdetail/MemberDetailUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/orgdashboard/memberdetail/MemberDetailUseCase.java index 55a75c5..0435d1e 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/orgdashboard/memberdetail/MemberDetailUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/orgdashboard/memberdetail/MemberDetailUseCase.java @@ -17,8 +17,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import org.jboss.logging.Logger; public class MemberDetailUseCase { + private static final Logger LOGGER = Logger.getLogger(MemberDetailUseCase.class); private final TaskRepository taskRepository; private final UserOrganizationRepository userOrganizationRepository; @@ -33,11 +35,17 @@ public class MemberDetailUseCase { } public MemberDetailOutput execute(MemberDetailInput input) { + LOGGER.infof("Calculating member detail for member %d in organization %d requested by %s from %s to %s", + input.memberId(), input.organizationId(), input.userEmail(), input.startDate(), input.endDate()); authorizer.execute(input.userEmail(), input.organizationId()); UserOrganization member = userOrganizationRepository .findByUserAndOrganization(input.memberId(), input.organizationId()) - .orElseThrow(() -> new IllegalArgumentException("Member not found in organization")); + .orElseThrow(() -> { + LOGGER.warnf("Member detail failed because member %d was not found in organization %d", + input.memberId(), input.organizationId()); + return new IllegalArgumentException("Member not found in organization"); + }); LocalDateTime start = input.startDate(); LocalDateTime end = input.endDate(); @@ -114,8 +122,11 @@ public class MemberDetailUseCase { } } - return MemberDetailMapper.toOutput(member, groupByDay ? "DAY" : "MONTH", periodStats, + MemberDetailOutput output = MemberDetailMapper.toOutput(member, groupByDay ? "DAY" : "MONTH", periodStats, new ArrayList<>(projectStatsMap.values())); + LOGGER.infof("Member detail calculated for member %d in organization %d with %d worked tasks and %d finished tasks", + input.memberId(), input.organizationId(), workedTasks.size(), finishedTasks.size()); + return output; } private boolean isWithinPeriod(TaskHistoryItem item, LocalDateTime start, LocalDateTime end) { diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/project/create/CreateProjectUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/project/create/CreateProjectUseCase.java index f203a20..7b49f59 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/project/create/CreateProjectUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/project/create/CreateProjectUseCase.java @@ -4,8 +4,10 @@ import net.beetechgroup.beetask.entities.Project; import net.beetechgroup.beetask.entities.organization.Organization; import net.beetechgroup.beetask.usecase.repository.OrganizationRepository; import net.beetechgroup.beetask.usecase.repository.ProjectRepository; +import org.jboss.logging.Logger; public class CreateProjectUseCase { + private static final Logger LOGGER = Logger.getLogger(CreateProjectUseCase.class); private final ProjectRepository projectRepository; private final OrganizationRepository organizationRepository; @@ -16,12 +18,18 @@ public class CreateProjectUseCase { public Project execute(Long organizationId, String projectName) { Organization organization = organizationRepository.findOrganizationById(organizationId) - .orElseThrow(() -> new RuntimeException("Organization not found")); + .orElseThrow(() -> { + LOGGER.warnf("Project creation failed because organization %d was not found", organizationId); + return new RuntimeException("Organization not found"); + }); + LOGGER.infof("Creating project '%s' in organization %d", projectName, organizationId); Project project = new Project(); project.setName(projectName); project.setOrganization(organization); - return projectRepository.saveProject(project); + Project savedProject = projectRepository.saveProject(project); + LOGGER.infof("Project %d created in organization %d", savedProject.getId(), organizationId); + return savedProject; } } diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/project/list/ListProjectsUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/project/list/ListProjectsUseCase.java index 236da7c..b7e4924 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/project/list/ListProjectsUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/project/list/ListProjectsUseCase.java @@ -4,8 +4,10 @@ import net.beetechgroup.beetask.entities.Project; import net.beetechgroup.beetask.usecase.repository.ProjectRepository; import java.util.List; +import org.jboss.logging.Logger; public class ListProjectsUseCase { + private static final Logger LOGGER = Logger.getLogger(ListProjectsUseCase.class); private final ProjectRepository projectRepository; public ListProjectsUseCase(ProjectRepository projectRepository) { @@ -13,6 +15,8 @@ public class ListProjectsUseCase { } public List execute(Long organizationId) { - return projectRepository.findByOrganizationId(organizationId); + List projects = projectRepository.findByOrganizationId(organizationId); + LOGGER.infof("Loaded %d projects for organization %d", projects.size(), organizationId); + return projects; } } diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/task/create/CreateTaskUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/task/create/CreateTaskUseCase.java index c6c1732..097d2fd 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/task/create/CreateTaskUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/task/create/CreateTaskUseCase.java @@ -6,8 +6,10 @@ import net.beetechgroup.beetask.entities.task.Task; import net.beetechgroup.beetask.usecase.repository.ProjectRepository; import net.beetechgroup.beetask.usecase.repository.TaskRepository; import net.beetechgroup.beetask.usecase.repository.UserRepository; +import org.jboss.logging.Logger; public class CreateTaskUseCase { + private static final Logger LOGGER = Logger.getLogger(CreateTaskUseCase.class); private final TaskRepository taskRepository; private final ProjectRepository projectRepository; private final UserRepository userRepository; @@ -19,6 +21,7 @@ public class CreateTaskUseCase { } public CreateTaskOutput execute(CreateTaskInput input) { + LOGGER.infof("Creating task '%s' for user %s", input.title(), input.userEmail()); Task task = new Task(); task.setTitle(input.title()); task.setDescription(input.description()); @@ -26,13 +29,21 @@ public class CreateTaskUseCase { if (Objects.nonNull(input.projectId())) { Project project = projectRepository.findProjectById(input.projectId()) - .orElseThrow(() -> new IllegalArgumentException("Projeto não encontrado com ID: " + input.projectId())); + .orElseThrow(() -> { + LOGGER.warnf("Task creation failed because project %d was not found", input.projectId()); + return new IllegalArgumentException("Projeto não encontrado com ID: " + input.projectId()); + }); task.setProject(project); } task.setUser(userRepository.findByEmail(input.userEmail()) - .orElseThrow(() -> new IllegalArgumentException("Usuário não encontrado: " + input.userEmail()))); + .orElseThrow(() -> { + LOGGER.warnf("Task creation failed because user %s was not found", input.userEmail()); + return new IllegalArgumentException("Usuário não encontrado: " + input.userEmail()); + })); - return CreateTaskMapper.toCreateTaskOutput(taskRepository.saveTask(task)); + CreateTaskOutput output = CreateTaskMapper.toCreateTaskOutput(taskRepository.saveTask(task)); + LOGGER.infof("Task %d created for user %s", output.id(), input.userEmail()); + return output; } } diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/task/get/GetTaskUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/task/get/GetTaskUseCase.java index ef65a66..5bba8f0 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/task/get/GetTaskUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/task/get/GetTaskUseCase.java @@ -3,8 +3,10 @@ package net.beetechgroup.beetask.usecase.task.get; import net.beetechgroup.beetask.usecase.repository.TaskRepository; import net.beetechgroup.beetask.usecase.task.create.CreateTaskMapper; import net.beetechgroup.beetask.usecase.task.create.CreateTaskOutput; +import org.jboss.logging.Logger; public class GetTaskUseCase { + private static final Logger LOGGER = Logger.getLogger(GetTaskUseCase.class); private final TaskRepository taskRepository; public GetTaskUseCase(TaskRepository taskRepository) { @@ -13,6 +15,7 @@ public class GetTaskUseCase { public CreateTaskOutput execute(Long id) { //TODO deve validar se o usuario logado é o dono da task + LOGGER.infof("Fetching task %d", id); return CreateTaskMapper.toCreateTaskOutput(taskRepository.findTaskById(id)); } } diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/task/listall/ListAllTasksUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/task/listall/ListAllTasksUseCase.java index eb6731a..92ba076 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/task/listall/ListAllTasksUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/task/listall/ListAllTasksUseCase.java @@ -3,10 +3,12 @@ package net.beetechgroup.beetask.usecase.task.listall; import net.beetechgroup.beetask.usecase.repository.TaskRepository; import net.beetechgroup.beetask.usecase.task.create.CreateTaskMapper; import net.beetechgroup.beetask.usecase.task.create.CreateTaskOutput; +import org.jboss.logging.Logger; import java.util.List; public class ListAllTasksUseCase { + private static final Logger LOGGER = Logger.getLogger(ListAllTasksUseCase.class); private final TaskRepository taskRepository; @@ -15,6 +17,8 @@ public class ListAllTasksUseCase { } public List execute() { - return taskRepository.findAllTasks().stream().map(CreateTaskMapper::toCreateTaskOutput).toList(); + List tasks = taskRepository.findAllTasks().stream().map(CreateTaskMapper::toCreateTaskOutput).toList(); + LOGGER.infof("Loaded %d tasks from repository", tasks.size()); + return tasks; } } diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/task/listall/ListMyTasksUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/task/listall/ListMyTasksUseCase.java index 120a9e0..b0934a6 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/task/listall/ListMyTasksUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/task/listall/ListMyTasksUseCase.java @@ -3,10 +3,12 @@ package net.beetechgroup.beetask.usecase.task.listall; import net.beetechgroup.beetask.usecase.repository.TaskRepository; import net.beetechgroup.beetask.usecase.task.create.CreateTaskMapper; import net.beetechgroup.beetask.usecase.task.create.CreateTaskOutput; +import org.jboss.logging.Logger; import java.util.List; public class ListMyTasksUseCase { + private static final Logger LOGGER = Logger.getLogger(ListMyTasksUseCase.class); private final TaskRepository taskRepository; @@ -15,6 +17,8 @@ public class ListMyTasksUseCase { } public List execute(String email) { - return taskRepository.findTasksByUser(email).stream().map(CreateTaskMapper::toCreateTaskOutput).toList(); + List tasks = taskRepository.findTasksByUser(email).stream().map(CreateTaskMapper::toCreateTaskOutput).toList(); + LOGGER.infof("Loaded %d tasks for user %s", tasks.size(), email); + return tasks; } } diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/task/start/StartTaskUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/task/start/StartTaskUseCase.java index 4a94ede..ba7e37b 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/task/start/StartTaskUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/task/start/StartTaskUseCase.java @@ -6,8 +6,10 @@ import net.beetechgroup.beetask.usecase.repository.TaskRepository; import net.beetechgroup.beetask.usecase.repository.UserRepository; import net.beetechgroup.beetask.usecase.task.create.CreateTaskMapper; import net.beetechgroup.beetask.usecase.task.create.CreateTaskOutput; +import org.jboss.logging.Logger; public class StartTaskUseCase { + private static final Logger LOGGER = Logger.getLogger(StartTaskUseCase.class); private final TaskRepository taskRepository; private final UserRepository userRepository; @@ -18,13 +20,18 @@ public class StartTaskUseCase { } public CreateTaskOutput execute(StartTaskInput input) { + LOGGER.infof("Starting task %d for user %s", input.id(), input.userEmail()); Task task = taskRepository.findTaskById(input.id()); if (Objects.nonNull(task.getUser()) && !task.getUser().getEmail().equals(input.userEmail())) { + LOGGER.warnf("User %s attempted to start task %d owned by %s", + input.userEmail(), input.id(), task.getUser().getEmail()); throw new SecurityException("Você não tem permissão para iniciar esta tarefa."); } task.start(); - return CreateTaskMapper.toCreateTaskOutput(taskRepository.saveTask(task)); + CreateTaskOutput output = CreateTaskMapper.toCreateTaskOutput(taskRepository.saveTask(task)); + LOGGER.infof("Task %d started successfully", input.id()); + return output; } } diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/task/stop/StopTaskUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/task/stop/StopTaskUseCase.java index d93c3e1..d71e385 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/task/stop/StopTaskUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/task/stop/StopTaskUseCase.java @@ -4,8 +4,10 @@ import net.beetechgroup.beetask.entities.task.Task; import net.beetechgroup.beetask.usecase.repository.TaskRepository; import net.beetechgroup.beetask.usecase.task.create.CreateTaskMapper; import net.beetechgroup.beetask.usecase.task.create.CreateTaskOutput; +import org.jboss.logging.Logger; public class StopTaskUseCase { + private static final Logger LOGGER = Logger.getLogger(StopTaskUseCase.class); private final TaskRepository taskRepository; @@ -14,8 +16,11 @@ public class StopTaskUseCase { } public CreateTaskOutput execute(StopTaskInput input) { + LOGGER.infof("Stopping task %d", input.id()); Task task = taskRepository.findTaskById(input.id()); task.stop(); - return CreateTaskMapper.toCreateTaskOutput(taskRepository.saveTask(task)); + CreateTaskOutput output = CreateTaskMapper.toCreateTaskOutput(taskRepository.saveTask(task)); + LOGGER.infof("Task %d stopped successfully", input.id()); + return output; } } diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/task/update/UpdateTaskStatusUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/task/update/UpdateTaskStatusUseCase.java index c7de9b1..5155bbe 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/task/update/UpdateTaskStatusUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/task/update/UpdateTaskStatusUseCase.java @@ -7,8 +7,10 @@ import net.beetechgroup.beetask.entities.task.TaskStatus; import net.beetechgroup.beetask.usecase.repository.TaskRepository; import net.beetechgroup.beetask.usecase.task.create.CreateTaskMapper; import net.beetechgroup.beetask.usecase.task.create.CreateTaskOutput; +import org.jboss.logging.Logger; public class UpdateTaskStatusUseCase { + private static final Logger LOGGER = Logger.getLogger(UpdateTaskStatusUseCase.class); private final TaskRepository taskRepository; @@ -17,10 +19,13 @@ public class UpdateTaskStatusUseCase { } public CreateTaskOutput execute(Long id, TaskStatus newStatus) { + LOGGER.infof("Updating status for task %d to %s", id, newStatus); Task task = taskRepository.findTaskById(id); task.setStatus(newStatus); - - return CreateTaskMapper.toCreateTaskOutput(taskRepository.saveTask(task)); + + CreateTaskOutput output = CreateTaskMapper.toCreateTaskOutput(taskRepository.saveTask(task)); + LOGGER.infof("Status updated for task %d to %s", id, newStatus); + return output; } } diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/task/update/UpdateTaskUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/task/update/UpdateTaskUseCase.java index 7eafc15..8c9f86a 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/task/update/UpdateTaskUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/task/update/UpdateTaskUseCase.java @@ -10,8 +10,10 @@ import net.beetechgroup.beetask.usecase.repository.TaskRepository; import net.beetechgroup.beetask.usecase.repository.UserRepository; import net.beetechgroup.beetask.usecase.task.create.CreateTaskMapper; import net.beetechgroup.beetask.usecase.task.create.CreateTaskOutput; +import org.jboss.logging.Logger; public class UpdateTaskUseCase { + private static final Logger LOGGER = Logger.getLogger(UpdateTaskUseCase.class); private final TaskRepository taskRepository; private final ProjectRepository projectRepository; private final UserRepository userRepository; @@ -23,11 +25,15 @@ public class UpdateTaskUseCase { } public CreateTaskOutput execute(UpdateTaskInput input) { + LOGGER.infof("Updating task %d requested by user %s", input.id(), input.userEmail()); Task task = taskRepository.findTaskById(input.id()); if (Objects.isNull(task.getUser())) { task.setUser(userRepository.findByEmail(input.userEmail()) - .orElseThrow(() -> new IllegalArgumentException("Usuário não encontrado: " + input.userEmail()))); + .orElseThrow(() -> { + LOGGER.warnf("Task update failed because user %s was not found", input.userEmail()); + return new IllegalArgumentException("Usuário não encontrado: " + input.userEmail()); + })); } task.setTitle(input.title()); @@ -36,7 +42,10 @@ public class UpdateTaskUseCase { if (Objects.nonNull(input.projectId())) { Project project = projectRepository.findProjectById(input.projectId()) - .orElseThrow(() -> new IllegalArgumentException("Projeto não encontrado com ID: " + input.projectId())); + .orElseThrow(() -> { + LOGGER.warnf("Task update failed because project %d was not found", input.projectId()); + return new IllegalArgumentException("Projeto não encontrado com ID: " + input.projectId()); + }); task.setProject(project); } else { task.setProject(null); @@ -69,8 +78,11 @@ public class UpdateTaskUseCase { task.getHistory().clear(); task.getHistory().addAll(updatedHistory); + LOGGER.infof("Task %d history updated with %d entries", input.id(), updatedHistory.size()); } - return CreateTaskMapper.toCreateTaskOutput(taskRepository.saveTask(task)); + CreateTaskOutput output = CreateTaskMapper.toCreateTaskOutput(taskRepository.saveTask(task)); + LOGGER.infof("Task %d updated successfully", input.id()); + return output; } } diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/user/create/CreateUserUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/user/create/CreateUserUseCase.java index e9b00bb..8726bd4 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/user/create/CreateUserUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/user/create/CreateUserUseCase.java @@ -2,8 +2,10 @@ package net.beetechgroup.beetask.usecase.user.create; import net.beetechgroup.beetask.entities.User; import net.beetechgroup.beetask.usecase.repository.UserRepository; +import org.jboss.logging.Logger; public class CreateUserUseCase { + private static final Logger LOGGER = Logger.getLogger(CreateUserUseCase.class); private final UserRepository userRepository; @@ -13,11 +15,14 @@ public class CreateUserUseCase { public CreateUserOutput execute(CreateUserInput input) { if (userRepository.findByEmail(input.email()).isPresent()) { + LOGGER.warnf("User creation rejected because email %s already exists", input.email()); throw new RuntimeException("User already exists with this email"); } + LOGGER.infof("Creating user with email %s", input.email()); User user = CreateUserMapper.toUser(input); User savedUser = userRepository.save(user); + LOGGER.infof("User %d created successfully with email %s", savedUser.getId(), savedUser.getEmail()); return CreateUserMapper.toCreateUserOutput(savedUser); } diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/user/profile/GetUserProfileUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/user/profile/GetUserProfileUseCase.java index 53bf753..f785660 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/user/profile/GetUserProfileUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/user/profile/GetUserProfileUseCase.java @@ -8,8 +8,10 @@ import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; +import org.jboss.logging.Logger; public class GetUserProfileUseCase { + private static final Logger LOGGER = Logger.getLogger(GetUserProfileUseCase.class); private final UserRepository userRepository; @@ -21,11 +23,13 @@ public class GetUserProfileUseCase { Optional userOptional = userRepository.findByEmail(email); if (userOptional.isEmpty()) { + LOGGER.warnf("Profile lookup failed because user %s was not found", email); throw new RuntimeException("User not found"); } User user = userOptional.get(); List userOrganizations = userRepository.findUserOrganizations(user.getId()); + LOGGER.infof("Loaded profile for user %s with %d active organizations", email, userOrganizations.size()); List orgs = userOrganizations.stream() .collect(Collectors.groupingBy(uo -> uo.getOrganization().getId())) diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml new file mode 100644 index 0000000..79fd9dd --- /dev/null +++ b/frontend/pnpm-lock.yaml @@ -0,0 +1,2166 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@hello-pangea/dnd': + specifier: ^18.0.1 + version: 18.0.1(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@tailwindcss/vite': + specifier: ^4.2.4 + version: 4.3.0(vite@8.0.11(@types/node@24.12.3)(jiti@2.7.0)) + clsx: + specifier: ^2.1.1 + version: 2.1.1 + date-fns: + specifier: ^4.1.0 + version: 4.1.0 + framer-motion: + specifier: ^12.38.0 + version: 12.38.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + lucide-react: + specifier: ^1.14.0 + version: 1.14.0(react@19.2.6) + react: + specifier: ^19.2.5 + version: 19.2.6 + react-dom: + specifier: ^19.2.5 + version: 19.2.6(react@19.2.6) + react-router-dom: + specifier: ^7.9.4 + version: 7.15.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + tailwind-merge: + specifier: ^3.5.0 + version: 3.6.0 + devDependencies: + '@eslint/js': + specifier: ^10.0.1 + version: 10.0.1(eslint@10.3.0(jiti@2.7.0)) + '@types/node': + specifier: ^24.12.2 + version: 24.12.3 + '@types/react': + specifier: ^19.2.14 + version: 19.2.14 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.14) + '@vitejs/plugin-react': + specifier: ^6.0.1 + version: 6.0.1(vite@8.0.11(@types/node@24.12.3)(jiti@2.7.0)) + autoprefixer: + specifier: ^10.5.0 + version: 10.5.0(postcss@8.5.14) + eslint: + specifier: ^10.2.1 + version: 10.3.0(jiti@2.7.0) + eslint-plugin-react-hooks: + specifier: ^7.1.1 + version: 7.1.1(eslint@10.3.0(jiti@2.7.0)) + eslint-plugin-react-refresh: + specifier: ^0.5.2 + version: 0.5.2(eslint@10.3.0(jiti@2.7.0)) + globals: + specifier: ^17.5.0 + version: 17.6.0 + postcss: + specifier: ^8.5.12 + version: 8.5.14 + tailwindcss: + specifier: ^4.2.4 + version: 4.3.0 + typescript: + specifier: ~6.0.2 + version: 6.0.3 + typescript-eslint: + specifier: ^8.58.2 + version: 8.59.2(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3) + vite: + specifier: ^8.0.10 + version: 8.0.11(@types/node@24.12.3)(jiti@2.7.0) + +packages: + + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.3': + resolution: {integrity: sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.2': + resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.3': + resolution: {integrity: sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@emnapi/core@1.10.0': + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.23.5': + resolution: {integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/config-helpers@0.5.5': + resolution: {integrity: sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/core@1.2.1': + resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/js@10.0.1': + resolution: {integrity: sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + peerDependencies: + eslint: ^10.0.0 + peerDependenciesMeta: + eslint: + optional: true + + '@eslint/object-schema@3.0.5': + resolution: {integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/plugin-kit@0.7.1': + resolution: {integrity: sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@hello-pangea/dnd@18.0.1': + resolution: {integrity: sha512-xojVWG8s/TGrKT1fC8K2tIWeejJYTAeJuj36zM//yEm/ZrnZUSFGS15BpO+jGZT1ybWvyXmeDJwPYb4dhWlbZQ==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@napi-rs/wasm-runtime@1.1.4': + resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + + '@oxc-project/types@0.128.0': + resolution: {integrity: sha512-huv1Y/LzBJkBVHt3OlC7u0zHBW9qXf1FdD7sGmc1rXc2P1mTwHssYv7jyGx5KAACSCH+9B3Bhn6Z9luHRvf7pQ==} + + '@rolldown/binding-android-arm64@1.0.0-rc.18': + resolution: {integrity: sha512-lIDyUAfD7U3+BWKzdxMbJcsYHuqXqmGz40aeRqvuAm3y5TkJSYTBW2RDrn65DJFPQqVjUAUqq5uz8urzQ8aBdQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.0.0-rc.18': + resolution: {integrity: sha512-apJq2ktnGp27nSInMR5Vcj8kY6xJzDAvfdIFlpDcAK/w4cDO58qVoi1YQsES/SKiFNge/6e4CUzgjfHduYqWpQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.0.0-rc.18': + resolution: {integrity: sha512-5Ofot8xbs+pxRHJqm9/9N/4sTQOvdrwEsmPE9pdLEEoAbdZtG6F2LMDfO1sp6ZAtXJuJV/21ew2srq3W8NXB5g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.0.0-rc.18': + resolution: {integrity: sha512-7h8eeOTT1eyqJyx64BFCnWZpNm486hGWt2sqeLLgDxA0xI1oGZ9H7gK1S85uNGmBhkdPwa/6reTxfFFKvIsebw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.18': + resolution: {integrity: sha512-eRcm/HVt9U/JFu5RKAEKwGQYtDCKWLiaH6wOnsSEp6NMBb/3Os8LgHZlNyzMpFVNmiiMFlfb2zEnebfzJrHFmg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.18': + resolution: {integrity: sha512-SOrT/cT4ukTmgnrEz/Hg3m7LBnuCLW9psDeMKrimRWY4I8DmnO7Lco8W2vtqPmMkbVu8iJ+g4GFLVLLOVjJ9DQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.18': + resolution: {integrity: sha512-QWjdxN1HJCpBTAcZ5N5F7wju3gVPzRzSpmGzx7na0c/1qpN9CFil+xt+l9lV/1M6/gqHSNXCiqPfwhVJPeLnug==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.18': + resolution: {integrity: sha512-ugCOyj7a4d9h3q9B+wXmf6g3a68UsjGh6dob5DHevHGMwDUbhsYNbSPxJsENcIttJZ9jv7qGM2UesLw5jqIhdg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.18': + resolution: {integrity: sha512-kKWRhbsotpXkGbcd5dllUWg5gEXcDAa8u5YnP9AV5DYNbvJHGzzuwv7dpmhc8NqKMJldl0a+x76IHbspEpEmdA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.18': + resolution: {integrity: sha512-uCo8ElcCIAMyYAZyuIZ81oFkhTSIllNvUCHCAlbhlN4ji3uC28h7IIdlXyIvGO7HsuqnV9p3rD/bpH7XhIyhRw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.0.0-rc.18': + resolution: {integrity: sha512-XNOQZtuE6yUIvx4rwGemwh8kpL1xvU41FXy/s9K7T/3JVcqGzo3NfKM2HrbrGgfPYGFW42f07Wk++aOC6B9NWA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.0.0-rc.18': + resolution: {integrity: sha512-tSn/kzrfa7tNOXr7sEacDBN4YsIqTyLqh45IO0nHDwtpKIDNDJr+VFojt+4klSpChxB29JLyduSsE0MKEwa65A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.0.0-rc.18': + resolution: {integrity: sha512-+J9YGmc+czgqlhYmwun3S3O0FIZhsH8ep2456xwjAdIOmuJxM7xz4P4PtrxU+Bz17a/5bqPA8o3HAAoX0teUdg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.18': + resolution: {integrity: sha512-zsu47DgU0FQzSwi6sU9dZoEdUv7pc1AptSEz/Z8HBg54sV0Pbs3N0+CrIbTsgiu6EyoaNN9CHboqbLaz9lhOyQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.18': + resolution: {integrity: sha512-7H+3yqGgmnlDTRRhw/xpYY9J1kf4GC681nVc4GqKhExZTDrVVrV2tsOR9kso0fvgBdcTCcQShx4SLLoHgaLwhg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.0-rc.18': + resolution: {integrity: sha512-CUY5Mnhe64xQBGZEEXQ5WyZwsc1JU3vAZLIxtrsBt3LO6UOb+C8GunVKqe9sT8NeWb4lqSaoJtp2xo6GxT1MNw==} + + '@rolldown/pluginutils@1.0.0-rc.7': + resolution: {integrity: sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==} + + '@tailwindcss/node@4.3.0': + resolution: {integrity: sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==} + + '@tailwindcss/oxide-android-arm64@4.3.0': + resolution: {integrity: sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.3.0': + resolution: {integrity: sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.3.0': + resolution: {integrity: sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.3.0': + resolution: {integrity: sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0': + resolution: {integrity: sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.0': + resolution: {integrity: sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.3.0': + resolution: {integrity: sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.3.0': + resolution: {integrity: sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.3.0': + resolution: {integrity: sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.3.0': + resolution: {integrity: sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.0': + resolution: {integrity: sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.3.0': + resolution: {integrity: sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.3.0': + resolution: {integrity: sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==} + engines: {node: '>= 20'} + + '@tailwindcss/vite@4.3.0': + resolution: {integrity: sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 || ^8 + + '@tybys/wasm-util@0.10.2': + resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + + '@types/esrecurse@4.3.1': + resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@24.12.3': + resolution: {integrity: sha512-8oljBDGun9cIsZRJR6fkihn0TSXJI0UDOOhncYaERq6M0JMDoPLxyscwruJcb4GKS6dvK/d8xebYBg27h/duaQ==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.14': + resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} + + '@types/use-sync-external-store@0.0.6': + resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} + + '@typescript-eslint/eslint-plugin@8.59.2': + resolution: {integrity: sha512-j/bwmkBvHUtPNxzuWe5z6BEk3q54YRyGlBXkSsmfoih7zNrBvl5A9A98anlp/7JbyZcWIJ8KXo/3Tq/DjFLtuQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.59.2 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/parser@8.59.2': + resolution: {integrity: sha512-plR3pp6D+SSUn1HM7xvSkx12/DhoHInI2YF35KAcVFNZvlC0gtrWqx7Qq1oH2Ssgi0vlFRCTbP+DZc7B9+TtsQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/project-service@8.59.2': + resolution: {integrity: sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.59.2': + resolution: {integrity: sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.59.2': + resolution: {integrity: sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.59.2': + resolution: {integrity: sha512-nhqaj1nmTdVVl/BP5omXNRGO38jn5iosis2vbdmupF2txCf8ylWT8lx+JlvMYYVqzGVKtjojUFoQ3JRWK+mfzQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.59.2': + resolution: {integrity: sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.59.2': + resolution: {integrity: sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.59.2': + resolution: {integrity: sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.59.2': + resolution: {integrity: sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@vitejs/plugin-react@6.0.1': + resolution: {integrity: sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 + babel-plugin-react-compiler: ^1.0.0 + vite: ^8.0.0 + peerDependenciesMeta: + '@rolldown/plugin-babel': + optional: true + babel-plugin-react-compiler: + optional: true + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + + autoprefixer@10.5.0: + resolution: {integrity: sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + baseline-browser-mapping@2.10.29: + resolution: {integrity: sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + brace-expansion@5.0.6: + resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} + engines: {node: 18 || 20 || >=22} + + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + caniuse-lite@1.0.30001792: + resolution: {integrity: sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-box-model@1.2.1: + resolution: {integrity: sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + date-fns@4.1.0: + resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + electron-to-chromium@1.5.353: + resolution: {integrity: sha512-kOrWphBi8TOZyiJZqsgqIle0lw+tzmnQK83pV9dZUd01Nm2POECSyFQMAuarzZdYqQW7FH9RaYOuaRo3h+bQ3w==} + + enhanced-resolve@5.21.2: + resolution: {integrity: sha512-xe9vQb5kReirPUxgQrXA3ihgbCqssmTiM7cOZ+Gzu+VeGWgpV98lLZvp0dl4yriyAePcewxGUs9UpKD8PET9KQ==} + engines: {node: '>=10.13.0'} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-plugin-react-hooks@7.1.1: + resolution: {integrity: sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==} + engines: {node: '>=18'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0 + + eslint-plugin-react-refresh@0.5.2: + resolution: {integrity: sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==} + peerDependencies: + eslint: ^9 || ^10 + + eslint-scope@9.1.2: + resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@10.3.0: + resolution: {integrity: sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@11.2.0: + resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + + fraction.js@5.3.4: + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} + + framer-motion@12.38.0: + resolution: {integrity: sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@17.6.0: + resolution: {integrity: sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==} + engines: {node: '>=18'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lucide-react@1.14.0: + resolution: {integrity: sha512-+1mdWcfSJVUsaTIjN9zoezmUhfXo5l0vP7ekBMPo3jcS/aIkxHnXqAPsByszMZx/Y8oQBRJxJx5xg+RH3urzxA==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + + motion-dom@12.38.0: + resolution: {integrity: sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==} + + motion-utils@12.36.0: + resolution: {integrity: sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + node-releases@2.0.38: + resolution: {integrity: sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.5.14: + resolution: {integrity: sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + raf-schd@4.0.3: + resolution: {integrity: sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==} + + react-dom@19.2.6: + resolution: {integrity: sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==} + peerDependencies: + react: ^19.2.6 + + react-redux@9.2.0: + resolution: {integrity: sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==} + peerDependencies: + '@types/react': ^18.2.25 || ^19 + react: ^18.0 || ^19 + redux: ^5.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + redux: + optional: true + + react-router-dom@7.15.0: + resolution: {integrity: sha512-VcrVg64Fo8nwBvDscajG8gRTLIuTC6N50nb22l2HOOV4PTOHgoGp8mUjy9wLiHYoYTSYI36tUnXZgasSRFZorQ==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + + react-router@7.15.0: + resolution: {integrity: sha512-HW9vYwuM8f4yx66Izy8xfrzCM+SBJluoZcCbww9A1TySax11S5Vgw6fi3ZjMONw9J4gQwngL7PzkyIpJJpJ7RQ==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + peerDependenciesMeta: + react-dom: + optional: true + + react@19.2.6: + resolution: {integrity: sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==} + engines: {node: '>=0.10.0'} + + redux@5.0.1: + resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} + + rolldown@1.0.0-rc.18: + resolution: {integrity: sha512-phmyKBpuBdRYDf4hgyynGAYn/rDDe+iZXKVJ7WX5b1zQzpLkP5oJRPGsfJuHdzPMlyyEO/4sPW6yfSx2gf7lVg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.8.0: + resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==} + engines: {node: '>=10'} + hasBin: true + + set-cookie-parser@2.7.2: + resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + tailwind-merge@3.6.0: + resolution: {integrity: sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==} + + tailwindcss@4.3.0: + resolution: {integrity: sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==} + + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + engines: {node: '>=12.0.0'} + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typescript-eslint@8.59.2: + resolution: {integrity: sha512-pJw051uomb3ZeCzGTpRb8RbEqB5Y4WWet8gl/GcTlU35BSx0PVdZ86/bqkQCyKKuraVQEK7r6kBHQXF+fBhkoQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + vite@8.0.11: + resolution: {integrity: sha512-Jz1mxtUBR5xTT65VOdJZUUeoyLtqljmFkiUXhPTLZka3RDc9vpi/xXkyrnsdRcm2lIi3l3GPMnAidTsEGIj3Ow==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.1.18 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + +snapshots: + + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.3': {} + + '@babel/core@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helpers': 7.29.2 + '@babel/parser': 7.29.3 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.3 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.29.2': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + + '@babel/parser@7.29.3': + dependencies: + '@babel/types': 7.29.0 + + '@babel/runtime@7.29.2': {} + + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.3 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@emnapi/core@1.10.0': + dependencies: + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.10.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@eslint-community/eslint-utils@4.9.1(eslint@10.3.0(jiti@2.7.0))': + dependencies: + eslint: 10.3.0(jiti@2.7.0) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.23.5': + dependencies: + '@eslint/object-schema': 3.0.5 + debug: 4.4.3 + minimatch: 10.2.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.5.5': + dependencies: + '@eslint/core': 1.2.1 + + '@eslint/core@1.2.1': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/js@10.0.1(eslint@10.3.0(jiti@2.7.0))': + optionalDependencies: + eslint: 10.3.0(jiti@2.7.0) + + '@eslint/object-schema@3.0.5': {} + + '@eslint/plugin-kit@0.7.1': + dependencies: + '@eslint/core': 1.2.1 + levn: 0.4.1 + + '@hello-pangea/dnd@18.0.1(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + dependencies: + '@babel/runtime': 7.29.2 + css-box-model: 1.2.1 + raf-schd: 4.0.3 + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + react-redux: 9.2.0(@types/react@19.2.14)(react@19.2.6)(redux@5.0.1) + redux: 5.0.1 + transitivePeerDependencies: + - '@types/react' + + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.2 + optional: true + + '@oxc-project/types@0.128.0': {} + + '@rolldown/binding-android-arm64@1.0.0-rc.18': + optional: true + + '@rolldown/binding-darwin-arm64@1.0.0-rc.18': + optional: true + + '@rolldown/binding-darwin-x64@1.0.0-rc.18': + optional: true + + '@rolldown/binding-freebsd-x64@1.0.0-rc.18': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.18': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.18': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.18': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.18': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.18': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.18': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.0-rc.18': + optional: true + + '@rolldown/binding-openharmony-arm64@1.0.0-rc.18': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.0-rc.18': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.18': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.18': + optional: true + + '@rolldown/pluginutils@1.0.0-rc.18': {} + + '@rolldown/pluginutils@1.0.0-rc.7': {} + + '@tailwindcss/node@4.3.0': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.21.2 + jiti: 2.7.0 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.3.0 + + '@tailwindcss/oxide-android-arm64@4.3.0': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.3.0': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.3.0': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.3.0': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.0': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.3.0': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.3.0': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.3.0': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.3.0': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.0': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.3.0': + optional: true + + '@tailwindcss/oxide@4.3.0': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.3.0 + '@tailwindcss/oxide-darwin-arm64': 4.3.0 + '@tailwindcss/oxide-darwin-x64': 4.3.0 + '@tailwindcss/oxide-freebsd-x64': 4.3.0 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.0 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.0 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.0 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.0 + '@tailwindcss/oxide-linux-x64-musl': 4.3.0 + '@tailwindcss/oxide-wasm32-wasi': 4.3.0 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.0 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.0 + + '@tailwindcss/vite@4.3.0(vite@8.0.11(@types/node@24.12.3)(jiti@2.7.0))': + dependencies: + '@tailwindcss/node': 4.3.0 + '@tailwindcss/oxide': 4.3.0 + tailwindcss: 4.3.0 + vite: 8.0.11(@types/node@24.12.3)(jiti@2.7.0) + + '@tybys/wasm-util@0.10.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/esrecurse@4.3.1': {} + + '@types/estree@1.0.9': {} + + '@types/json-schema@7.0.15': {} + + '@types/node@24.12.3': + dependencies: + undici-types: 7.16.0 + + '@types/react-dom@19.2.3(@types/react@19.2.14)': + dependencies: + '@types/react': 19.2.14 + + '@types/react@19.2.14': + dependencies: + csstype: 3.2.3 + + '@types/use-sync-external-store@0.0.6': {} + + '@typescript-eslint/eslint-plugin@8.59.2(@typescript-eslint/parser@8.59.2(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.59.2(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.59.2 + '@typescript-eslint/type-utils': 8.59.2(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/utils': 8.59.2(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.59.2 + eslint: 10.3.0(jiti@2.7.0) + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.59.2(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.59.2 + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/typescript-estree': 8.59.2(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.59.2 + debug: 4.4.3 + eslint: 10.3.0(jiti@2.7.0) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.59.2(typescript@6.0.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.59.2(typescript@6.0.3) + '@typescript-eslint/types': 8.59.2 + debug: 4.4.3 + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.59.2': + dependencies: + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/visitor-keys': 8.59.2 + + '@typescript-eslint/tsconfig-utils@8.59.2(typescript@6.0.3)': + dependencies: + typescript: 6.0.3 + + '@typescript-eslint/type-utils@8.59.2(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3)': + dependencies: + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/typescript-estree': 8.59.2(typescript@6.0.3) + '@typescript-eslint/utils': 8.59.2(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3) + debug: 4.4.3 + eslint: 10.3.0(jiti@2.7.0) + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.59.2': {} + + '@typescript-eslint/typescript-estree@8.59.2(typescript@6.0.3)': + dependencies: + '@typescript-eslint/project-service': 8.59.2(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.59.2(typescript@6.0.3) + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/visitor-keys': 8.59.2 + debug: 4.4.3 + minimatch: 10.2.5 + semver: 7.8.0 + tinyglobby: 0.2.16 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.59.2(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0(jiti@2.7.0)) + '@typescript-eslint/scope-manager': 8.59.2 + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/typescript-estree': 8.59.2(typescript@6.0.3) + eslint: 10.3.0(jiti@2.7.0) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.59.2': + dependencies: + '@typescript-eslint/types': 8.59.2 + eslint-visitor-keys: 5.0.1 + + '@vitejs/plugin-react@6.0.1(vite@8.0.11(@types/node@24.12.3)(jiti@2.7.0))': + dependencies: + '@rolldown/pluginutils': 1.0.0-rc.7 + vite: 8.0.11(@types/node@24.12.3)(jiti@2.7.0) + + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + autoprefixer@10.5.0(postcss@8.5.14): + dependencies: + browserslist: 4.28.2 + caniuse-lite: 1.0.30001792 + fraction.js: 5.3.4 + picocolors: 1.1.1 + postcss: 8.5.14 + postcss-value-parser: 4.2.0 + + balanced-match@4.0.4: {} + + baseline-browser-mapping@2.10.29: {} + + brace-expansion@5.0.6: + dependencies: + balanced-match: 4.0.4 + + browserslist@4.28.2: + dependencies: + baseline-browser-mapping: 2.10.29 + caniuse-lite: 1.0.30001792 + electron-to-chromium: 1.5.353 + node-releases: 2.0.38 + update-browserslist-db: 1.2.3(browserslist@4.28.2) + + caniuse-lite@1.0.30001792: {} + + clsx@2.1.1: {} + + convert-source-map@2.0.0: {} + + cookie@1.1.1: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-box-model@1.2.1: + dependencies: + tiny-invariant: 1.3.3 + + csstype@3.2.3: {} + + date-fns@4.1.0: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + detect-libc@2.1.2: {} + + electron-to-chromium@1.5.353: {} + + enhanced-resolve@5.21.2: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-plugin-react-hooks@7.1.1(eslint@10.3.0(jiti@2.7.0)): + dependencies: + '@babel/core': 7.29.0 + '@babel/parser': 7.29.3 + eslint: 10.3.0(jiti@2.7.0) + hermes-parser: 0.25.1 + zod: 4.4.3 + zod-validation-error: 4.0.2(zod@4.4.3) + transitivePeerDependencies: + - supports-color + + eslint-plugin-react-refresh@0.5.2(eslint@10.3.0(jiti@2.7.0)): + dependencies: + eslint: 10.3.0(jiti@2.7.0) + + eslint-scope@9.1.2: + dependencies: + '@types/esrecurse': 4.3.1 + '@types/estree': 1.0.9 + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@10.3.0(jiti@2.7.0): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0(jiti@2.7.0)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.23.5 + '@eslint/config-helpers': 0.5.5 + '@eslint/core': 1.2.1 + '@eslint/plugin-kit': 0.7.1 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 + ajv: 6.15.0 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 9.1.2 + eslint-visitor-keys: 5.0.1 + espree: 11.2.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + minimatch: 10.2.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.7.0 + transitivePeerDependencies: + - supports-color + + espree@11.2.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 5.0.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + + flatted@3.4.2: {} + + fraction.js@5.3.4: {} + + framer-motion@12.38.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + dependencies: + motion-dom: 12.38.0 + motion-utils: 12.36.0 + tslib: 2.8.1 + optionalDependencies: + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + + fsevents@2.3.3: + optional: true + + gensync@1.0.0-beta.2: {} + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@17.6.0: {} + + graceful-fs@4.2.11: {} + + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + imurmurhash@0.1.4: {} + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + isexe@2.0.0: {} + + jiti@2.7.0: {} + + js-tokens@4.0.0: {} + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@2.2.3: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lucide-react@1.14.0(react@19.2.6): + dependencies: + react: 19.2.6 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.6 + + motion-dom@12.38.0: + dependencies: + motion-utils: 12.36.0 + + motion-utils@12.36.0: {} + + ms@2.1.3: {} + + nanoid@3.3.12: {} + + natural-compare@1.4.0: {} + + node-releases@2.0.38: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + picocolors@1.1.1: {} + + picomatch@4.0.4: {} + + postcss-value-parser@4.2.0: {} + + postcss@8.5.14: + dependencies: + nanoid: 3.3.12 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + punycode@2.3.1: {} + + raf-schd@4.0.3: {} + + react-dom@19.2.6(react@19.2.6): + dependencies: + react: 19.2.6 + scheduler: 0.27.0 + + react-redux@9.2.0(@types/react@19.2.14)(react@19.2.6)(redux@5.0.1): + dependencies: + '@types/use-sync-external-store': 0.0.6 + react: 19.2.6 + use-sync-external-store: 1.6.0(react@19.2.6) + optionalDependencies: + '@types/react': 19.2.14 + redux: 5.0.1 + + react-router-dom@7.15.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + dependencies: + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + react-router: 7.15.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + + react-router@7.15.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + dependencies: + cookie: 1.1.1 + react: 19.2.6 + set-cookie-parser: 2.7.2 + optionalDependencies: + react-dom: 19.2.6(react@19.2.6) + + react@19.2.6: {} + + redux@5.0.1: {} + + rolldown@1.0.0-rc.18: + dependencies: + '@oxc-project/types': 0.128.0 + '@rolldown/pluginutils': 1.0.0-rc.18 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.0-rc.18 + '@rolldown/binding-darwin-arm64': 1.0.0-rc.18 + '@rolldown/binding-darwin-x64': 1.0.0-rc.18 + '@rolldown/binding-freebsd-x64': 1.0.0-rc.18 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.18 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.18 + '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.18 + '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.18 + '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.18 + '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.18 + '@rolldown/binding-linux-x64-musl': 1.0.0-rc.18 + '@rolldown/binding-openharmony-arm64': 1.0.0-rc.18 + '@rolldown/binding-wasm32-wasi': 1.0.0-rc.18 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.18 + '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.18 + + scheduler@0.27.0: {} + + semver@6.3.1: {} + + semver@7.8.0: {} + + set-cookie-parser@2.7.2: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + source-map-js@1.2.1: {} + + tailwind-merge@3.6.0: {} + + tailwindcss@4.3.0: {} + + tapable@2.3.3: {} + + tiny-invariant@1.3.3: {} + + tinyglobby@0.2.16: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + ts-api-utils@2.5.0(typescript@6.0.3): + dependencies: + typescript: 6.0.3 + + tslib@2.8.1: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typescript-eslint@8.59.2(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.59.2(@typescript-eslint/parser@8.59.2(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/parser': 8.59.2(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.59.2(typescript@6.0.3) + '@typescript-eslint/utils': 8.59.2(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3) + eslint: 10.3.0(jiti@2.7.0) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + + typescript@6.0.3: {} + + undici-types@7.16.0: {} + + update-browserslist-db@1.2.3(browserslist@4.28.2): + dependencies: + browserslist: 4.28.2 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + use-sync-external-store@1.6.0(react@19.2.6): + dependencies: + react: 19.2.6 + + vite@8.0.11(@types/node@24.12.3)(jiti@2.7.0): + dependencies: + lightningcss: 1.32.0 + picomatch: 4.0.4 + postcss: 8.5.14 + rolldown: 1.0.0-rc.18 + tinyglobby: 0.2.16 + optionalDependencies: + '@types/node': 24.12.3 + fsevents: 2.3.3 + jiti: 2.7.0 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + yallist@3.1.1: {} + + yocto-queue@0.1.0: {} + + zod-validation-error@4.0.2(zod@4.4.3): + dependencies: + zod: 4.4.3 + + zod@4.4.3: {}