From 7d2778739d4aba56480347f610013a6a66a53586 Mon Sep 17 00:00:00 2001 From: Paulo Gomes da Cruz Junior Date: Wed, 11 Dec 2024 05:03:29 -0800 Subject: [PATCH] fix(SILVA-515): updating submission trends (#525) --- .../oracle/endpoint/UserActionsEndpoint.java | 50 + .../entity/OpeningTrendsProjection.java | 14 + .../oracle/repository/OpeningRepository.java | 31 + .../oracle/service/OpeningTrendsService.java | 94 ++ .../postgres/dto/OpeningsPerYearDto.java | 5 +- .../endpoint/DashboardMetricsEndpoint.java | 38 - .../service/DashboardMetricsService.java | 97 +- ...OpeningSearchEndpointIntegrationTest.java} | 3 +- .../UserActionsEndpointIntegrationTest.java | 110 ++ .../service/OpeningTrendsServiceTest.java | 209 ++++ .../DashboardMetricsEndpointTest.java | 32 - .../service/DashboardMetricsServiceTest.java | 136 +-- frontend/.vscode/settings.json | 3 +- frontend/package-lock.json | 947 +++++++++--------- frontend/package.json | 4 +- .../components/BarChartGrouped.test.tsx | 225 ++++- .../components/BarChartTooltip.test.tsx | 50 + .../components/OpeningMetricsTab.test.tsx | 24 +- .../screens/SilvicultureSearch.test.tsx | 89 ++ .../__test__/services/OpeningService.test.ts | 12 +- .../src/components/ActionButtons/index.tsx | 2 +- .../BarChartGrouped/BarChartGrouped.scss | 3 +- .../src/components/BarChartGrouped/index.tsx | 374 ++++--- .../src/components/BarChartTooltip/index.scss | 18 + .../src/components/BarChartTooltip/index.tsx | 27 + .../Openings/OpeningsSearchTab/index.tsx | 54 +- .../src/screens/SilvicultureSearch/index.tsx | 17 +- frontend/src/services/OpeningService.ts | 48 +- frontend/src/services/apiConfig.ts | 7 +- frontend/src/services/search/openings.ts | 27 +- frontend/src/types/OpeningPerYearChart.ts | 3 + frontend/src/types/OpeningTypes.ts | 4 +- frontend/tsconfig.json | 2 +- frontend/vite.config.ts | 40 +- frontend/vitest.config.ts | 29 - 35 files changed, 1809 insertions(+), 1019 deletions(-) create mode 100644 backend/src/main/java/ca/bc/gov/restapi/results/oracle/entity/OpeningTrendsProjection.java create mode 100644 backend/src/main/java/ca/bc/gov/restapi/results/oracle/service/OpeningTrendsService.java rename backend/src/test/java/ca/bc/gov/restapi/results/oracle/endpoint/{OpeningSearchEndpointTest.java => OpeningSearchEndpointIntegrationTest.java} (98%) create mode 100644 backend/src/test/java/ca/bc/gov/restapi/results/oracle/endpoint/UserActionsEndpointIntegrationTest.java create mode 100644 backend/src/test/java/ca/bc/gov/restapi/results/oracle/service/OpeningTrendsServiceTest.java create mode 100644 frontend/src/__test__/components/BarChartTooltip.test.tsx create mode 100644 frontend/src/__test__/screens/SilvicultureSearch.test.tsx create mode 100644 frontend/src/components/BarChartTooltip/index.scss create mode 100644 frontend/src/components/BarChartTooltip/index.tsx delete mode 100644 frontend/vitest.config.ts diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/endpoint/UserActionsEndpoint.java b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/endpoint/UserActionsEndpoint.java index 59f7e96e..514ba241 100644 --- a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/endpoint/UserActionsEndpoint.java +++ b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/endpoint/UserActionsEndpoint.java @@ -1,13 +1,17 @@ package ca.bc.gov.restapi.results.oracle.endpoint; +import ca.bc.gov.restapi.results.oracle.service.OpeningTrendsService; import ca.bc.gov.restapi.results.oracle.service.UserActionsService; import ca.bc.gov.restapi.results.postgres.dto.MyRecentActionsRequestsDto; +import ca.bc.gov.restapi.results.postgres.dto.OpeningsPerYearDto; +import java.time.LocalDate; import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @@ -17,6 +21,7 @@ public class UserActionsEndpoint { private final UserActionsService userActionsService; + private final OpeningTrendsService openingTrendsService; @GetMapping("/recent-actions") public ResponseEntity> getUserRecentOpeningsActions() { @@ -32,4 +37,49 @@ public ResponseEntity> getUserRecentOpeningsAct return ResponseEntity.ok(actionsDto); } + /** + * Gets data for the Opening submission trends Chart (Openings per year) on the Dashboard SILVA + * page. + * + * @param orgUnits Optional district code filter. + * @param statusCodes Optional opening status code filter. + * @param entryDateStart Optional opening entry timestamp start date filter. + * @param entryDateEnd Optional opening entry timestamp end date filter. + * @return A list of values to populate the chart or 204 no content if no data. + */ + @GetMapping("/submission-trends") + public ResponseEntity> getOpeningsSubmissionTrends( + @RequestParam(value = "orgUnitCode", required = false) + List orgUnits, + @RequestParam(value = "statusCode", required = false) + List statusCodes, + @RequestParam(value = "entryDateStart", required = false) + LocalDate entryDateStart, + @RequestParam(value = "entryDateEnd", required = false) + LocalDate entryDateEnd + ) { + + List resultList = + openingTrendsService.getOpeningSubmissionTrends( + getDateOrDefault(entryDateStart,LocalDate.now().minusYears(1)), + getDateOrDefault(entryDateEnd, + //If we have an end date, we get it, otherwise we use the current date, + // and no matter if we have the start date or not, we add a year to the end date + getDateOrDefault(entryDateStart,LocalDate.now().minusYears(1)).plusYears(1) + ), + orgUnits, + statusCodes + ); + + if (resultList.isEmpty()) { + return ResponseEntity.noContent().build(); + } + + return ResponseEntity.ok(resultList); + } + + private LocalDate getDateOrDefault(LocalDate date, LocalDate defaultDate) { + return date != null ? date : defaultDate; + } + } diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/entity/OpeningTrendsProjection.java b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/entity/OpeningTrendsProjection.java new file mode 100644 index 00000000..12a8a556 --- /dev/null +++ b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/entity/OpeningTrendsProjection.java @@ -0,0 +1,14 @@ +package ca.bc.gov.restapi.results.oracle.entity; + +import java.time.LocalDateTime; + +public interface OpeningTrendsProjection { + Long getOpeningId(); + String getUserId(); + LocalDateTime getEntryTimestamp(); + LocalDateTime getUpdateTimestamp(); + String getStatus(); + String getOrgUnitCode(); + String getOrgUnitName(); + String getClientNumber(); +} diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/repository/OpeningRepository.java b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/repository/OpeningRepository.java index b8da7a77..b6069729 100644 --- a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/repository/OpeningRepository.java +++ b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/repository/OpeningRepository.java @@ -8,6 +8,7 @@ import ca.bc.gov.restapi.results.oracle.dto.DashboardStockingEventDto; import ca.bc.gov.restapi.results.oracle.dto.OpeningSearchFiltersDto; import ca.bc.gov.restapi.results.oracle.entity.OpeningEntity; +import ca.bc.gov.restapi.results.oracle.entity.OpeningTrendsProjection; import ca.bc.gov.restapi.results.oracle.entity.SilvicultureSearchProjection; import java.util.List; import org.springframework.data.domain.Page; @@ -404,4 +405,34 @@ Page searchByOpeningIds( List openingIds, Pageable pageable ); + + @Query( + nativeQuery = true, + value = """ + SELECT + o.OPENING_ID, + o.ENTRY_USERID as user_id, + o.ENTRY_TIMESTAMP, + o.UPDATE_TIMESTAMP, + o.OPENING_STATUS_CODE as status, + ou.ORG_UNIT_CODE AS org_unit_code, + ou.ORG_UNIT_NAME AS org_unit_name, + res.CLIENT_NUMBER AS client_number + FROM THE.OPENING o + LEFT JOIN THE.ORG_UNIT ou ON (ou.ORG_UNIT_NO = o.ADMIN_DISTRICT_NO) + LEFT JOIN THE.RESULTS_ELECTRONIC_SUBMISSION res ON (res.RESULTS_SUBMISSION_ID = o.RESULTS_SUBMISSION_ID) + WHERE + ( + o.ENTRY_TIMESTAMP BETWEEN TO_TIMESTAMP(:startDate, 'YYYY-MM-DD') AND TO_TIMESTAMP(:endDate, 'YYYY-MM-DD') + OR o.UPDATE_TIMESTAMP BETWEEN TO_TIMESTAMP(:startDate, 'YYYY-MM-DD') AND TO_TIMESTAMP(:endDate, 'YYYY-MM-DD') + ) + AND ( 'NOVALUE' in (:statusList) OR o.OPENING_STATUS_CODE IN (:statusList) ) + AND ( 'NOVALUE' in (:orgUnitList) OR ou.ORG_UNIT_CODE IN (:orgUnitList) ) + ORDER BY o.ENTRY_TIMESTAMP""") + List getOpeningTrends( + String startDate, + String endDate, + List statusList, + List orgUnitList + ); } diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/service/OpeningTrendsService.java b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/service/OpeningTrendsService.java new file mode 100644 index 00000000..0046cd3f --- /dev/null +++ b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/service/OpeningTrendsService.java @@ -0,0 +1,94 @@ +package ca.bc.gov.restapi.results.oracle.service; + +import ca.bc.gov.restapi.results.oracle.entity.OpeningTrendsProjection; +import ca.bc.gov.restapi.results.oracle.repository.OpeningRepository; +import ca.bc.gov.restapi.results.postgres.dto.OpeningsPerYearDto; +import java.time.LocalDate; +import java.time.Month; +import java.time.YearMonth; +import java.time.format.DateTimeFormatter; +import java.time.format.TextStyle; +import java.time.temporal.ChronoUnit; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class OpeningTrendsService { + + private final OpeningRepository openingRepository; + + public List getOpeningSubmissionTrends( + LocalDate startDate, + LocalDate endDate, + List orgUnits, + List statusCodes + ) { + + // if the difference between the start date and the end date is bigger than 12, thrown an exception + if (ChronoUnit.MONTHS.between(startDate, endDate) > 12) { + throw new IllegalArgumentException("The date range must be within 12 months"); + } + + List entities = + openingRepository.getOpeningTrends( + startDate.format(DateTimeFormatter.ISO_DATE), + endDate.format(DateTimeFormatter.ISO_DATE), + statusCodes == null ? List.of("NOVALUE") : statusCodes, + orgUnits == null ? List.of("NOVALUE") : orgUnits + ); + + if (entities.isEmpty()) { + log.info("No Opening Submission Trends data found!"); + return List.of(); + } + + // Group by month and status + Map> monthToStatusCountMap = entities.stream() + .filter(entity -> entity.getEntryTimestamp() != null) // Ensure timestamp is not null + .collect(Collectors.groupingBy( + entity -> entity.getEntryTimestamp().getMonthValue(), // Extract month value + Collectors.groupingBy( + OpeningTrendsProjection::getStatus, // Group by status + Collectors.counting() // Count occurrences + ) + )); + + // Map to count total entries grouped by month + Map monthToCountMap = monthToStatusCountMap.entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, // Month + entry -> entry.getValue().values().stream().mapToLong(Long::longValue).sum() // Sum counts per status + )); + + // Generate a 12-month sequence starting from the start date + List yearMonths = IntStream.range(0, 12) // Always 12 months + .mapToObj(offset -> YearMonth.from(startDate).plusMonths(offset)) + .toList(); + + // Generate the DTOs in the custom order + return yearMonths.stream() + .map(yearMonth -> new OpeningsPerYearDto( + yearMonth.getMonthValue(), + yearMonth.getYear(), + getMonthName(yearMonth.getMonthValue()), + monthToCountMap.getOrDefault(yearMonth.getMonthValue(), 0L), // Total count for the month + monthToStatusCountMap.getOrDefault(yearMonth.getMonthValue(), Collections.emptyMap()) // Status counts map + )) + .toList(); + } + + + private String getMonthName(int month) { + return Month.of(month).getDisplayName(TextStyle.SHORT, Locale.CANADA); + } + +} diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/postgres/dto/OpeningsPerYearDto.java b/backend/src/main/java/ca/bc/gov/restapi/results/postgres/dto/OpeningsPerYearDto.java index d00dcc6c..235b7e8c 100644 --- a/backend/src/main/java/ca/bc/gov/restapi/results/postgres/dto/OpeningsPerYearDto.java +++ b/backend/src/main/java/ca/bc/gov/restapi/results/postgres/dto/OpeningsPerYearDto.java @@ -1,5 +1,6 @@ package ca.bc.gov.restapi.results.postgres.dto; +import java.util.Map; import lombok.Builder; import lombok.With; @@ -10,8 +11,10 @@ @With public record OpeningsPerYearDto( Integer month, + Integer year, String monthName, - Integer amount + Long amount, + Map statusCounts ) { } diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/postgres/endpoint/DashboardMetricsEndpoint.java b/backend/src/main/java/ca/bc/gov/restapi/results/postgres/endpoint/DashboardMetricsEndpoint.java index 1542d5f7..50bed80e 100644 --- a/backend/src/main/java/ca/bc/gov/restapi/results/postgres/endpoint/DashboardMetricsEndpoint.java +++ b/backend/src/main/java/ca/bc/gov/restapi/results/postgres/endpoint/DashboardMetricsEndpoint.java @@ -3,7 +3,6 @@ import ca.bc.gov.restapi.results.common.util.TimestampUtil; import ca.bc.gov.restapi.results.postgres.dto.DashboardFiltersDto; import ca.bc.gov.restapi.results.postgres.dto.FreeGrowingMilestonesDto; -import ca.bc.gov.restapi.results.postgres.dto.OpeningsPerYearDto; import ca.bc.gov.restapi.results.postgres.service.DashboardMetricsService; import java.util.List; import lombok.RequiredArgsConstructor; @@ -23,43 +22,6 @@ public class DashboardMetricsEndpoint { private final DashboardMetricsService dashboardMetricsService; - /** - * Gets data for the Opening submission trends Chart (Openings per year) on the Dashboard SILVA - * page. - * - * @param orgUnitCode Optional district code filter. - * @param statusCode Optional opening status code filter. - * @param entryDateStart Optional opening entry timestamp start date filter. - * @param entryDateEnd Optional opening entry timestamp end date filter. - * @return A list of values to populate the chart or 204 no content if no data. - */ - @GetMapping("/submission-trends") - public ResponseEntity> getOpeningsSubmissionTrends( - @RequestParam(value = "orgUnitCode", required = false) - String orgUnitCode, - @RequestParam(value = "statusCode", required = false) - String statusCode, - @RequestParam(value = "entryDateStart", required = false) - String entryDateStart, - @RequestParam(value = "entryDateEnd", required = false) - String entryDateEnd) { - DashboardFiltersDto filtersDto = - new DashboardFiltersDto( - orgUnitCode, - statusCode, - TimestampUtil.parseDateString(entryDateStart), - TimestampUtil.parseDateString(entryDateEnd), - null); - - List resultList = - dashboardMetricsService.getOpeningsSubmissionTrends(filtersDto); - - if (resultList.isEmpty()) { - return ResponseEntity.noContent().build(); - } - - return ResponseEntity.ok(resultList); - } /** * Gets data for the Free growing Chart on the Dashboard SILVA page. diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/postgres/service/DashboardMetricsService.java b/backend/src/main/java/ca/bc/gov/restapi/results/postgres/service/DashboardMetricsService.java index 9e4c62fb..3b9ffa74 100644 --- a/backend/src/main/java/ca/bc/gov/restapi/results/postgres/service/DashboardMetricsService.java +++ b/backend/src/main/java/ca/bc/gov/restapi/results/postgres/service/DashboardMetricsService.java @@ -6,17 +6,13 @@ import ca.bc.gov.restapi.results.postgres.dto.DashboardFiltersDto; import ca.bc.gov.restapi.results.postgres.dto.FreeGrowingMilestonesDto; import ca.bc.gov.restapi.results.postgres.dto.MyRecentActionsRequestsDto; -import ca.bc.gov.restapi.results.postgres.dto.OpeningsPerYearDto; import ca.bc.gov.restapi.results.postgres.entity.OpeningsActivityEntity; import ca.bc.gov.restapi.results.postgres.entity.OpeningsLastYearEntity; import ca.bc.gov.restapi.results.postgres.repository.OpeningsActivityRepository; import ca.bc.gov.restapi.results.postgres.repository.OpeningsLastYearRepository; import java.math.BigDecimal; import java.math.RoundingMode; -import java.time.LocalDateTime; -import java.time.Month; import java.util.ArrayList; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -28,7 +24,9 @@ import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; -/** This class contains methods for gathering and grouping data for the dashboard metrics screen. */ +/** + * This class contains methods for gathering and grouping data for the dashboard metrics screen. + */ @Slf4j @Service @RequiredArgsConstructor @@ -42,41 +40,6 @@ public class DashboardMetricsService { private final PrettyTime prettyTime = new PrettyTime(); - /** - * Get openings submission trends data for the opening per year chart. - * - * @param filters Possible filter, see {@link DashboardFiltersDto} for more. - * @return A list of {@link OpeningsPerYearDto} for the opening chart. - */ - public List getOpeningsSubmissionTrends(DashboardFiltersDto filters) { - log.info("Getting Opening Submission Trends with filters {}", filters.toString()); - - LocalDateTime baseDateTime = LocalDateTime.now().minusMonths(12); - List entities = - openingsLastYearRepository.findAllFromLastYear( - baseDateTime, Sort.by("entryTimestamp").ascending()); - - if (entities.isEmpty()) { - log.info("No Opening Submission Trends data found!"); - return List.of(); - } - - Map monthNamesMap = new HashMap<>(); - Map> resultMap = - createBaseMonthsMap(monthNamesMap, entities.get(0).getEntryTimestamp().getMonthValue()); - - filterOpeningSubmissions(resultMap, entities, filters); - - List chartData = new ArrayList<>(); - for (Integer monthKey : resultMap.keySet()) { - List monthDataList = resultMap.get(monthKey); - String monthName = monthNamesMap.get(monthKey); - log.info("Value {} for the month: {}", monthDataList.size(), monthName); - chartData.add(new OpeningsPerYearDto(monthKey, monthName, monthDataList.size())); - } - - return chartData; - } /** * Get free growing milestone declarations data for the chart. @@ -210,58 +173,4 @@ public List getUserRecentOpeningsActions() { .toList(); } - private void filterOpeningSubmissions( - Map> resultMap, - List entities, - DashboardFiltersDto filters) { - // Iterate over the found records filtering and putting them into the right month - for (OpeningsLastYearEntity entity : entities) { - // Org Unit filter - District - if (!Objects.isNull(filters.orgUnit()) - && !filters.orgUnit().equals(entity.getOrgUnitCode())) { - continue; - } - - // Status filter - if (!Objects.isNull(filters.status()) && !filters.status().equals(entity.getStatus())) { - continue; - } - - // Entry start date filter - if (!Objects.isNull(filters.entryDateStart()) - && entity.getEntryTimestamp().isBefore(filters.entryDateStart())) { - continue; - } - - // Entry end date filter - if (!Objects.isNull(filters.entryDateEnd()) - && entity.getEntryTimestamp().isAfter(filters.entryDateEnd())) { - continue; - } - - resultMap.get(entity.getEntryTimestamp().getMonthValue()).add(entity); - } - } - - private Map> createBaseMonthsMap( - Map monthNamesMap, Integer firstMonth) { - Map> resultMap = new LinkedHashMap<>(); - - // Fill with 12 months - log.info("First month: {}", firstMonth); - while (resultMap.size() < 12) { - resultMap.put(firstMonth, new ArrayList<>()); - - String monthName = Month.of(firstMonth).name().toLowerCase(); - monthName = monthName.substring(0, 1).toUpperCase() + monthName.substring(1, 3); - monthNamesMap.put(firstMonth, monthName); - - firstMonth += 1; - if (firstMonth == 13) { - firstMonth = 1; - } - } - - return resultMap; - } } diff --git a/backend/src/test/java/ca/bc/gov/restapi/results/oracle/endpoint/OpeningSearchEndpointTest.java b/backend/src/test/java/ca/bc/gov/restapi/results/oracle/endpoint/OpeningSearchEndpointIntegrationTest.java similarity index 98% rename from backend/src/test/java/ca/bc/gov/restapi/results/oracle/endpoint/OpeningSearchEndpointTest.java rename to backend/src/test/java/ca/bc/gov/restapi/results/oracle/endpoint/OpeningSearchEndpointIntegrationTest.java index 1436b965..1aa4f1cc 100644 --- a/backend/src/test/java/ca/bc/gov/restapi/results/oracle/endpoint/OpeningSearchEndpointTest.java +++ b/backend/src/test/java/ca/bc/gov/restapi/results/oracle/endpoint/OpeningSearchEndpointIntegrationTest.java @@ -28,7 +28,7 @@ @AutoConfigureMockMvc @WithMockJwt @DisplayName("Integrated Test | Opening Search Endpoint") -class OpeningSearchEndpointTest extends AbstractTestContainerIntegrationTest { +class OpeningSearchEndpointIntegrationTest extends AbstractTestContainerIntegrationTest { @Autowired private MockMvc mockMvc; @@ -194,7 +194,6 @@ void getOpeningOrgUnitsByCode_happyPath_shouldSucceed() throws Exception { @Test @DisplayName("Get Opening Org Units By Code not Found should Succeed") void getOpeningOrgUnitsByCode_notFound_shouldSucceed() throws Exception { - //when(orgUnitService.findAllOrgUnitsByCode(List.of("DAS"))).thenReturn(List.of()); mockMvc .perform( diff --git a/backend/src/test/java/ca/bc/gov/restapi/results/oracle/endpoint/UserActionsEndpointIntegrationTest.java b/backend/src/test/java/ca/bc/gov/restapi/results/oracle/endpoint/UserActionsEndpointIntegrationTest.java new file mode 100644 index 00000000..8b7dc72e --- /dev/null +++ b/backend/src/test/java/ca/bc/gov/restapi/results/oracle/endpoint/UserActionsEndpointIntegrationTest.java @@ -0,0 +1,110 @@ +package ca.bc.gov.restapi.results.oracle.endpoint; + +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import ca.bc.gov.restapi.results.extensions.AbstractTestContainerIntegrationTest; +import ca.bc.gov.restapi.results.extensions.WithMockJwt; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +@AutoConfigureMockMvc +@WithMockJwt +@DisplayName("Integrated Test | User Actions Endpoint") +class UserActionsEndpointIntegrationTest extends AbstractTestContainerIntegrationTest { + + @Autowired + private MockMvc mockMvc; + + @Test + @DisplayName("User recent actions requests test with data should succeed") + void getUserRecentOpeningsActions_withData_shouldSucceed() throws Exception { + + mockMvc + .perform( + get("/api/users/recent-actions") + .with(csrf().asHeader()) + .header("Content-Type", MediaType.APPLICATION_JSON_VALUE) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$[0].activityType").value("Submitted")) + .andExpect(jsonPath("$[0].openingId").value("102")) + .andExpect(jsonPath("$[0].statusCode").value("FTML")) + .andExpect(jsonPath("$[0].statusDescription").value("Forest Tenure - Major Licensee")) + .andReturn(); + } + + @Test + @DisplayName("User recent actions requests test no data should succeed") + @WithMockJwt(value = "no-data") + void getUserRecentOpeningsActions_noData_shouldSucceed() throws Exception { + + mockMvc + .perform( + get("/api/users/recent-actions") + .with(csrf().asHeader()) + .header("Content-Type", MediaType.APPLICATION_JSON_VALUE) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isNoContent()) + .andReturn(); + } + + @Test + @DisplayName("Openings submission trends happy path should succeed") + void getOpeningsSubmissionTrends_happyPath_shouldSucceed() throws Exception { + + mockMvc.perform(get("/api/users/submission-trends") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$[0].month").value("12")) + .andExpect(jsonPath("$[0].amount").value(0)) + .andExpect(jsonPath("$[1].monthName").value("Jan")) + .andExpect(jsonPath("$[1].amount").value(3)); + } + + @Test + @DisplayName("Openings submission trends no data should return no content") + void getOpeningsSubmissionTrends_noData_shouldReturnNoContent() throws Exception { + + mockMvc.perform(get("/api/users/submission-trends") + .param("orgUnitCode", "ORG1") + .param("statusCode", "STATUS1") + .param("entryDateStart", "2022-01-01") + .param("entryDateEnd", "2023-01-01") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isNoContent()); + } + + @Test + @DisplayName("Openings submission trends with filters should succeed") + void getOpeningsSubmissionTrends_withFilters_shouldSucceed() throws Exception { + + mockMvc.perform(get("/api/users/submission-trends") + .param("statusCode", "APP") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$[0].month").value("12")) + .andExpect(jsonPath("$[0].amount").value(0)); + } + + @Test + @DisplayName("Openings submission trends with invalid date range should return no content") + void getOpeningsSubmissionTrends_invalidDateRange_shouldReturnNoContent() throws Exception { + + mockMvc.perform(get("/api/users/submission-trends") + .param("entryDateStart", "2023-01-01") + .param("entryDateEnd", "2022-01-01") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isNoContent()); + } +} \ No newline at end of file diff --git a/backend/src/test/java/ca/bc/gov/restapi/results/oracle/service/OpeningTrendsServiceTest.java b/backend/src/test/java/ca/bc/gov/restapi/results/oracle/service/OpeningTrendsServiceTest.java new file mode 100644 index 00000000..03dcb9da --- /dev/null +++ b/backend/src/test/java/ca/bc/gov/restapi/results/oracle/service/OpeningTrendsServiceTest.java @@ -0,0 +1,209 @@ +package ca.bc.gov.restapi.results.oracle.service; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import ca.bc.gov.restapi.results.oracle.entity.OpeningTrendsProjection; +import ca.bc.gov.restapi.results.oracle.repository.OpeningRepository; +import ca.bc.gov.restapi.results.postgres.dto.OpeningsPerYearDto; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.TextStyle; +import java.util.List; +import java.util.Locale; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +@DisplayName("Unit Test | Opening Trends Service") +class OpeningTrendsServiceTest { + + @Mock + OpeningRepository openingRepository; + private OpeningTrendsService openingTrendsService; + + @BeforeEach + void setUp() { + openingTrendsService = new OpeningTrendsService(openingRepository); + } + + private List mockOpeningsEntityList() { + LocalDateTime entryTimestamp = LocalDateTime.now(); + + OpeningTrendsProjection entity = new TestOpeningTrendsProjection( + 123456L, + "userId", + entryTimestamp, + entryTimestamp, + "APP", + "DCR", + "District Code", + "00012797" + ); + return List.of(entity); + } + + @Test + @DisplayName("Opening submission trends with no filters should succeed") + void getOpeningSubmissionTrends_noFilters_shouldSucceed() { + LocalDateTime now = LocalDateTime.now(); + List entities = mockOpeningsEntityList(); + when(openingRepository.getOpeningTrends(any(), any(), any(), any())).thenReturn(entities); + + List list = openingTrendsService.getOpeningSubmissionTrends( + LocalDate.now(), + LocalDate.now(), + null, + null + ); + + String monthName = now.getMonth().name().toLowerCase(); + monthName = monthName.substring(0, 1).toUpperCase() + monthName.substring(1, 3); + + Assertions.assertFalse(list.isEmpty()); + Assertions.assertEquals(12, list.size()); + Assertions.assertEquals(now.getMonthValue(), list.get(0).month()); + Assertions.assertEquals(monthName, list.get(0).monthName()); + Assertions.assertEquals(1, list.get(0).amount()); + } + + @Test + @DisplayName("Opening submission trends with Org Unit filter should succeed") + void getOpeningSubmissionTrends_orgUnitFilter_shouldSucceed() { + LocalDateTime now = LocalDateTime.now(); + List entities = mockOpeningsEntityList(); + when(openingRepository.getOpeningTrends(any(), any(), any(), any())).thenReturn(entities); + + List list = openingTrendsService.getOpeningSubmissionTrends( + now.toLocalDate(), + now.toLocalDate(), + List.of("AAA"), + null + ); + + String monthName = now.getMonth().getDisplayName(TextStyle.SHORT, Locale.CANADA); + + Assertions.assertFalse(list.isEmpty()); + Assertions.assertEquals(12, list.size()); + Assertions.assertEquals(now.getMonthValue(), list.get(0).month()); + Assertions.assertEquals(monthName, list.get(0).monthName()); + Assertions.assertEquals(1, list.get(0).amount()); + } + + @Test + @DisplayName("Opening submission trends with Status filter should succeed") + void getOpeningSubmissionTrends_statusFilter_shouldSucceed() { + LocalDateTime now = LocalDateTime.now(); + List entities = mockOpeningsEntityList(); + when(openingRepository.getOpeningTrends(any(), any(), any(), any())).thenReturn(entities); + + List list = openingTrendsService.getOpeningSubmissionTrends( + LocalDate.now(), + LocalDate.now(), + null, + List.of("APP") + ); + + String monthName = now.getMonth().name().toLowerCase(); + monthName = monthName.substring(0, 1).toUpperCase() + monthName.substring(1, 3); + + Assertions.assertFalse(list.isEmpty()); + Assertions.assertEquals(12, list.size()); + Assertions.assertEquals(now.getMonthValue(), list.get(0).month()); + Assertions.assertEquals(monthName, list.get(0).monthName()); + Assertions.assertEquals(1, list.get(0).amount()); + } + + @Test + @DisplayName("Opening submission trends with Status filter not matching should succeed") + void getOpeningSubmissionTrends_statusFilterNot_shouldSucceed() { + List entities = mockOpeningsEntityList(); + when(openingRepository.getOpeningTrends(any(), any(), any(), any())).thenReturn(entities); + + List list = openingTrendsService.getOpeningSubmissionTrends( + LocalDate.now(), + LocalDate.now().plusYears(1), + null, + List.of("UPD") + ); + Assertions.assertFalse(list.isEmpty()); + Assertions.assertEquals(12, list.size()); + Assertions.assertEquals(1, list.get(0).amount()); + Assertions.assertEquals(0, list.get(1).amount()); + Assertions.assertEquals(0, list.get(2).amount()); + Assertions.assertEquals(0, list.get(3).amount()); + Assertions.assertEquals(0, list.get(4).amount()); + Assertions.assertEquals(0, list.get(5).amount()); + Assertions.assertEquals(0, list.get(6).amount()); + Assertions.assertEquals(0, list.get(7).amount()); + Assertions.assertEquals(0, list.get(8).amount()); + Assertions.assertEquals(0, list.get(9).amount()); + Assertions.assertEquals(0, list.get(10).amount()); + Assertions.assertEquals(0, list.get(11).amount()); + } + + @Test + @DisplayName("Opening submission trends with Dates filter should succeed") + void getOpeningSubmissionTrends_datesFilter_shouldSucceed() { + List entities = mockOpeningsEntityList(); + when(openingRepository.getOpeningTrends(any(), any(), any(), any())).thenReturn(entities); + + LocalDate now = LocalDate.now(); + LocalDate oneMonthBefore = now.minusMonths(1L); + LocalDate oneMonthLater = now.plusMonths(1L); + + List list = openingTrendsService.getOpeningSubmissionTrends( + oneMonthBefore, + oneMonthLater, + null, + null + ); + + String monthName = oneMonthBefore.getMonth().getDisplayName(TextStyle.SHORT, Locale.CANADA); + Assertions.assertFalse(list.isEmpty()); + Assertions.assertEquals(12, list.size()); + Assertions.assertEquals(oneMonthBefore.getMonthValue(), list.get(0).month()); + Assertions.assertEquals(monthName, list.get(0).monthName()); + Assertions.assertEquals(1, list.get(1).amount()); + } + + @Test + @DisplayName("Opening submission trends with no data should succeed") + void getOpeningSubmissionTrends_noData_shouldSucceed() { + when(openingRepository.getOpeningTrends(any(), any(), any(), any())).thenReturn(List.of()); + + List list = openingTrendsService.getOpeningSubmissionTrends( + LocalDate.now(), + LocalDate.now(), + null, + null + ); + + Assertions.assertTrue(list.isEmpty()); + } + + + @Getter + @AllArgsConstructor + static + class TestOpeningTrendsProjection implements OpeningTrendsProjection { + + private Long openingId; + private String userId; + private LocalDateTime entryTimestamp; + private LocalDateTime updateTimestamp; + private String status; + private String orgUnitCode; + private String orgUnitName; + private String clientNumber; + + } + +} \ No newline at end of file diff --git a/backend/src/test/java/ca/bc/gov/restapi/results/postgres/endpoint/DashboardMetricsEndpointTest.java b/backend/src/test/java/ca/bc/gov/restapi/results/postgres/endpoint/DashboardMetricsEndpointTest.java index e5c8a315..c145e2bc 100644 --- a/backend/src/test/java/ca/bc/gov/restapi/results/postgres/endpoint/DashboardMetricsEndpointTest.java +++ b/backend/src/test/java/ca/bc/gov/restapi/results/postgres/endpoint/DashboardMetricsEndpointTest.java @@ -27,38 +27,6 @@ class DashboardMetricsEndpointTest extends AbstractTestContainerIntegrationTest @Autowired private MockMvc mockMvc; - @Test - @DisplayName("Opening submission trends with no filters should succeed") - void getOpeningsSubmissionTrends_noFilters_shouldSucceed() throws Exception { - - mockMvc - .perform( - get("/api/dashboard-metrics/submission-trends") - .with(csrf().asHeader()) - .header("Content-Type", MediaType.APPLICATION_JSON_VALUE) - .accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$[0].month").value("1")) - .andExpect(jsonPath("$[0].monthName").value("Jan")) - .andExpect(jsonPath("$[0].amount").value("1")) - .andReturn(); - } - - @Test - @DisplayName("Opening submission trends with no data should succeed") - void getOpeningsSubmissionTrends_orgUnitFilter_shouldSucceed() throws Exception { - - mockMvc - .perform( - get("/api/dashboard-metrics/submission-trends") - .with(csrf().asHeader()) - .header("Content-Type", MediaType.APPLICATION_JSON_VALUE) - .accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andReturn(); - } - @Test @DisplayName("Free growing milestones test with no filters should succeed") void getFreeGrowingMilestonesData_noFilters_shouldSucceed() throws Exception { diff --git a/backend/src/test/java/ca/bc/gov/restapi/results/postgres/service/DashboardMetricsServiceTest.java b/backend/src/test/java/ca/bc/gov/restapi/results/postgres/service/DashboardMetricsServiceTest.java index bd2b4459..31908848 100644 --- a/backend/src/test/java/ca/bc/gov/restapi/results/postgres/service/DashboardMetricsServiceTest.java +++ b/backend/src/test/java/ca/bc/gov/restapi/results/postgres/service/DashboardMetricsServiceTest.java @@ -1,13 +1,11 @@ package ca.bc.gov.restapi.results.postgres.service; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; import ca.bc.gov.restapi.results.common.security.LoggedUserService; import ca.bc.gov.restapi.results.postgres.dto.DashboardFiltersDto; import ca.bc.gov.restapi.results.postgres.dto.FreeGrowingMilestonesDto; import ca.bc.gov.restapi.results.postgres.dto.MyRecentActionsRequestsDto; -import ca.bc.gov.restapi.results.postgres.dto.OpeningsPerYearDto; import ca.bc.gov.restapi.results.postgres.entity.OpeningsActivityEntity; import ca.bc.gov.restapi.results.postgres.entity.OpeningsLastYearEntity; import ca.bc.gov.restapi.results.postgres.repository.OpeningsActivityRepository; @@ -28,11 +26,14 @@ @ExtendWith(MockitoExtension.class) class DashboardMetricsServiceTest { - @Mock OpeningsLastYearRepository openingsLastYearRepository; + @Mock + OpeningsLastYearRepository openingsLastYearRepository; - @Mock OpeningsActivityRepository openingsActivityRepository; + @Mock + OpeningsActivityRepository openingsActivityRepository; - @Mock LoggedUserService loggedUserService; + @Mock + LoggedUserService loggedUserService; private DashboardMetricsService dashboardMetricsService; @@ -59,126 +60,6 @@ void setup() { openingsLastYearRepository, openingsActivityRepository, loggedUserService); } - @Test - @DisplayName("Opening submission trends with no filters should succeed") - void getOpeningsSubmissionTrends_noFilters_shouldSucceed() throws Exception { - LocalDateTime now = LocalDateTime.now(); - List entities = mockOpeningsEntityList(); - when(openingsLastYearRepository.findAllFromLastYear(any(), any())).thenReturn(entities); - - DashboardFiltersDto filtersDto = new DashboardFiltersDto(null, null, null, null, null); - List list = dashboardMetricsService.getOpeningsSubmissionTrends(filtersDto); - - String monthName = now.getMonth().name().toLowerCase(); - monthName = monthName.substring(0, 1).toUpperCase() + monthName.substring(1, 3); - - Assertions.assertFalse(list.isEmpty()); - Assertions.assertEquals(12, list.size()); - Assertions.assertEquals(now.getMonthValue(), list.get(0).month()); - Assertions.assertEquals(monthName, list.get(0).monthName()); - Assertions.assertEquals(1, list.get(0).amount()); - } - - @Test - @DisplayName("Opening submission trends with Org Unit filter should succeed") - void getOpeningsSubmissionTrends_orgUnitFilter_shouldSucceed() throws Exception { - LocalDateTime now = LocalDateTime.now(); - List entities = mockOpeningsEntityList(); - when(openingsLastYearRepository.findAllFromLastYear(any(), any())).thenReturn(entities); - - DashboardFiltersDto filtersDto = new DashboardFiltersDto("AAA", null, null, null, null); - List list = dashboardMetricsService.getOpeningsSubmissionTrends(filtersDto); - - String monthName = now.getMonth().name().toLowerCase(); - monthName = monthName.substring(0, 1).toUpperCase() + monthName.substring(1, 3); - - Assertions.assertFalse(list.isEmpty()); - Assertions.assertEquals(12, list.size()); - Assertions.assertEquals(now.getMonthValue(), list.get(0).month()); - Assertions.assertEquals(monthName, list.get(0).monthName()); - Assertions.assertEquals(0, list.get(0).amount()); - } - - @Test - @DisplayName("Opening submission trends with Status filter should succeed") - void getOpeningsSubmissionTrends_statusFilter_shouldSucceed() { - LocalDateTime now = LocalDateTime.now(); - List entities = mockOpeningsEntityList(); - when(openingsLastYearRepository.findAllFromLastYear(any(), any())).thenReturn(entities); - - DashboardFiltersDto filtersDto = new DashboardFiltersDto(null, "APP", null, null, null); - List list = dashboardMetricsService.getOpeningsSubmissionTrends(filtersDto); - - String monthName = now.getMonth().name().toLowerCase(); - monthName = monthName.substring(0, 1).toUpperCase() + monthName.substring(1, 3); - - Assertions.assertFalse(list.isEmpty()); - Assertions.assertEquals(12, list.size()); - Assertions.assertEquals(now.getMonthValue(), list.get(0).month()); - Assertions.assertEquals(monthName, list.get(0).monthName()); - Assertions.assertEquals(1, list.get(0).amount()); - } - - @Test - @DisplayName("Opening submission trends with Status filter not matching should succeed") - void getOpeningsSubmissionTrends_statusFilterNot_shouldSucceed() { - List entities = mockOpeningsEntityList(); - when(openingsLastYearRepository.findAllFromLastYear(any(), any())).thenReturn(entities); - - DashboardFiltersDto filtersDto = new DashboardFiltersDto(null, "UPD", null, null, null); - List list = dashboardMetricsService.getOpeningsSubmissionTrends(filtersDto); - - Assertions.assertFalse(list.isEmpty()); - Assertions.assertEquals(12, list.size()); - Assertions.assertEquals(0, list.get(0).amount()); - Assertions.assertEquals(0, list.get(1).amount()); - Assertions.assertEquals(0, list.get(2).amount()); - Assertions.assertEquals(0, list.get(3).amount()); - Assertions.assertEquals(0, list.get(4).amount()); - Assertions.assertEquals(0, list.get(5).amount()); - Assertions.assertEquals(0, list.get(6).amount()); - Assertions.assertEquals(0, list.get(7).amount()); - Assertions.assertEquals(0, list.get(8).amount()); - Assertions.assertEquals(0, list.get(9).amount()); - Assertions.assertEquals(0, list.get(10).amount()); - Assertions.assertEquals(0, list.get(11).amount()); - } - - @Test - @DisplayName("Opening submission trends with Dates filter should succeed") - void getOpeningsSubmissionTrends_datesFilter_shouldSucceed() { - List entities = mockOpeningsEntityList(); - when(openingsLastYearRepository.findAllFromLastYear(any(), any())).thenReturn(entities); - - LocalDateTime now = LocalDateTime.now(); - LocalDateTime oneMonthBefore = now.minusMonths(1L); - LocalDateTime oneMonthLater = now.plusMonths(1L); - - DashboardFiltersDto filtersDto = - new DashboardFiltersDto(null, null, oneMonthBefore, oneMonthLater, null); - List list = dashboardMetricsService.getOpeningsSubmissionTrends(filtersDto); - - String monthName = now.getMonth().name().toLowerCase(); - monthName = monthName.substring(0, 1).toUpperCase() + monthName.substring(1, 3); - - Assertions.assertFalse(list.isEmpty()); - Assertions.assertEquals(12, list.size()); - Assertions.assertEquals(now.getMonthValue(), list.get(0).month()); - Assertions.assertEquals(monthName, list.get(0).monthName()); - Assertions.assertEquals(1, list.get(0).amount()); - } - - @Test - @DisplayName("Opening submission trends with no data should succeed") - void getOpeningsSubmissionTrends_noData_shouldSucceed() { - when(openingsLastYearRepository.findAllFromLastYear(any(), any())).thenReturn(List.of()); - - DashboardFiltersDto filtersDto = new DashboardFiltersDto(null, null, null, null, null); - List list = dashboardMetricsService.getOpeningsSubmissionTrends(filtersDto); - - Assertions.assertTrue(list.isEmpty()); - } - @Test @DisplayName("Free growing milestones without filters should succeed") void getFreeGrowingMilestoneChartData_noFilters_shouldSucceed() { @@ -370,7 +251,7 @@ void getUserRecentOpeningsActions_happyPath_shouldSucceed() { Sort sort = Sort.by("lastUpdated").descending(); when(openingsActivityRepository.findAllByEntryUserid(userId, - PageRequest.of(0,5,sort))) + PageRequest.of(0, 5, sort))) .thenReturn(List.of(activity)); List dtoList = @@ -393,7 +274,8 @@ void getUserRecentOpeningsActions_noData_shouldSucceed() { when(loggedUserService.getLoggedUserId()).thenReturn(userId); Sort sort = Sort.by("lastUpdated").descending(); - when(openingsActivityRepository.findAllByEntryUserid(userId, PageRequest.of(0,5,sort))).thenReturn(List.of()); + when(openingsActivityRepository.findAllByEntryUserid(userId, + PageRequest.of(0, 5, sort))).thenReturn(List.of()); List dtoList = dashboardMetricsService.getUserRecentOpeningsActions(); diff --git a/frontend/.vscode/settings.json b/frontend/.vscode/settings.json index 6fcf00da..16fee3a7 100644 --- a/frontend/.vscode/settings.json +++ b/frontend/.vscode/settings.json @@ -5,5 +5,6 @@ "titleBar.activeBackground": "#5d9857", "titleBar.inactiveBackground": "#5d9857", }, - "peacock.color": "#1857a4" + "peacock.color": "#1857a4", + "typescript.tsdk": "node_modules/typescript/lib" } \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 48b16aa8..8f90c42e 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@bcgov-nr/nr-theme": "^1.7.0", + "@carbon/charts": "^1.22.7", "@carbon/charts-react": "^1.22.5", "@carbon/icons-react": "^11.53.0", "@carbon/pictograms-react": "^11.69.0", @@ -50,6 +51,7 @@ "@types/qs": "^6.9.16", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", + "@types/testing-library__jest-dom": "^5.14.9", "@typescript-eslint/eslint-plugin": "^6.5.0", "@typescript-eslint/parser": "^6.5.0", "@vitest/coverage-v8": "^2.0.0", @@ -66,7 +68,7 @@ "sass": "^1.62.1", "sass-loader": "^16.0.0", "typescript": "^5.2.2", - "vitest": "^2.0.0" + "vitest": "^2.1.8" } }, "node_modules/@adobe/css-tools": { @@ -90,9 +92,9 @@ } }, "node_modules/@aws-amplify/analytics": { - "version": "7.0.60", - "resolved": "https://registry.npmjs.org/@aws-amplify/analytics/-/analytics-7.0.60.tgz", - "integrity": "sha512-7gCUlKcm7YxUYiT5twGbzsMN6pX82thS0yLf2jdZjuUIX5t5nBrRbqiSWhzGLu7VajRB4H//icf+s6b4Udhzsw==", + "version": "7.0.62", + "resolved": "https://registry.npmjs.org/@aws-amplify/analytics/-/analytics-7.0.62.tgz", + "integrity": "sha512-moCmxOpgAZSmT6C8bymXBZBWq7X1qY8cBbpjCFATwJPrO0tBuHBjI3pun6OrLJVA7hEwr7s17m6I+qAzwPg3Ag==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/client-firehose": "3.621.0", @@ -106,24 +108,24 @@ } }, "node_modules/@aws-amplify/api": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/@aws-amplify/api/-/api-6.1.5.tgz", - "integrity": "sha512-RDCbWCoyyCBF3lY0o1PKzKx2qqNJKdQf6PQEnFdKUtszY6iWGuX1RpDVTAZQHeKTjLlxQUrd/FYzIyBipyLU6A==", + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/@aws-amplify/api/-/api-6.1.7.tgz", + "integrity": "sha512-0dgEQtdPsD5CR0LrBQXGE6Qp9yfVn7TZ+PRi7E6pAUL2BfKam5yYJHjSQJNRFRT+Amp59XQyC//SPijgsR9F2g==", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/api-graphql": "4.6.3", - "@aws-amplify/api-rest": "4.0.60", + "@aws-amplify/api-graphql": "4.6.5", + "@aws-amplify/api-rest": "4.0.62", "tslib": "^2.5.0" } }, "node_modules/@aws-amplify/api-graphql": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/@aws-amplify/api-graphql/-/api-graphql-4.6.3.tgz", - "integrity": "sha512-9OWHNH21Mks3H5oUKXUtApkRM2+qa6a2Ht2uj5zpqbaalrUjlNHM4mm/3S37fEqAaKfQTtnie6uz3xdl6VTy0Q==", + "version": "4.6.5", + "resolved": "https://registry.npmjs.org/@aws-amplify/api-graphql/-/api-graphql-4.6.5.tgz", + "integrity": "sha512-noR3mY/TQYdL0nRfVAaX+f4PTWMUr2WG8FB4OlFHWZVz9woTpPViGzkCJnuYVCF/fCv6Qg2h+8GTumgr22OXbg==", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/api-rest": "4.0.60", - "@aws-amplify/core": "6.7.0", + "@aws-amplify/api-rest": "4.0.62", + "@aws-amplify/core": "6.7.2", "@aws-amplify/data-schema": "^1.7.0", "@aws-sdk/types": "3.387.0", "graphql": "15.8.0", @@ -133,9 +135,9 @@ } }, "node_modules/@aws-amplify/api-rest": { - "version": "4.0.60", - "resolved": "https://registry.npmjs.org/@aws-amplify/api-rest/-/api-rest-4.0.60.tgz", - "integrity": "sha512-wCjfgjzxbBGbnQI+D+KuHV6cuKFuxksULg/qjX0sXGeHJeyfu1L4s52pz6+11sEl01gvHBf/KzWyEu1irzaJEQ==", + "version": "4.0.62", + "resolved": "https://registry.npmjs.org/@aws-amplify/api-rest/-/api-rest-4.0.62.tgz", + "integrity": "sha512-6VLE25yxQhsxVqHQDmAoVC6yyvYWFA0JX8sibAFQpZ1liRd41j61fE9IcJGyWqtwIl179M8N2MJu/JWuILM9Yw==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.5.0" @@ -145,9 +147,9 @@ } }, "node_modules/@aws-amplify/auth": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/@aws-amplify/auth/-/auth-6.8.0.tgz", - "integrity": "sha512-QWGNVr3mOWmCnYbqvJkdv/eZyRNuultv7u9I6OTGVSD3jIEBEXDryaxZFROUpTi5Voto7yamTPLI+m0MUH+23Q==", + "version": "6.8.2", + "resolved": "https://registry.npmjs.org/@aws-amplify/auth/-/auth-6.8.2.tgz", + "integrity": "sha512-sxeNLSQqhR7QIgydldEB/t/CZ73R1ASkH9C1h1lmcdkJPckziIQsnV6ZQOMiK0Abbpldbm30CmqkjBcJ5IdQOg==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.5.0" @@ -157,9 +159,9 @@ } }, "node_modules/@aws-amplify/core": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/@aws-amplify/core/-/core-6.7.0.tgz", - "integrity": "sha512-JRzkRU6RNRjGuUj3LhWFr6gGYNTZ0SHLXrVzpiFOJmKL09AJBLsi+t+J+70MhYfqlReVKZRUJ1HkxRN0bmQvOg==", + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@aws-amplify/core/-/core-6.7.2.tgz", + "integrity": "sha512-RyLDimiP08znuW/qDVsWVqACT+SQo9M+XY+1q4qI4KzWUDhOScdttlrt4p51llVArmR6OjGMHGT9369Gt5dWCQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-js": "5.2.0", @@ -221,12 +223,12 @@ } }, "node_modules/@aws-amplify/datastore": { - "version": "5.0.62", - "resolved": "https://registry.npmjs.org/@aws-amplify/datastore/-/datastore-5.0.62.tgz", - "integrity": "sha512-scccoXKo1GB/mkpM5CYO9sIc0P42q9nkQvSe8hXpnM3Muqf6y0bFPqrYPROtJDCiRUcob3Z3gncFKOb6AIfGVA==", + "version": "5.0.64", + "resolved": "https://registry.npmjs.org/@aws-amplify/datastore/-/datastore-5.0.64.tgz", + "integrity": "sha512-cxdZfVIaTLvbj1b5EBzuIggVQnHZ2YeGwkVs79SYaYn4+MZkO3ED/3jn0mw9XFudSiScF5St+NIdEHelDQ4bWA==", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/api": "6.1.5", + "@aws-amplify/api": "6.1.7", "buffer": "4.9.2", "idb": "5.0.6", "immer": "9.0.6", @@ -238,9 +240,9 @@ } }, "node_modules/@aws-amplify/notifications": { - "version": "2.0.60", - "resolved": "https://registry.npmjs.org/@aws-amplify/notifications/-/notifications-2.0.60.tgz", - "integrity": "sha512-zhcEAEzhelT+ssK3/SpYVAxcqgkKa+YZM+MTvXgCfj4gZahWaN/yD6+wSfueol4nvF3ro8da7EgBdeOr2UWfCw==", + "version": "2.0.62", + "resolved": "https://registry.npmjs.org/@aws-amplify/notifications/-/notifications-2.0.62.tgz", + "integrity": "sha512-KALsOQb0PJUL6G9AlTiEf4qhvPudIjFwVecLAI1wimvVU7BaqutxqyafFlqUgvKB7L4V1w+9DlvfiZrO0zsAcw==", "license": "Apache-2.0", "dependencies": { "lodash": "^4.17.21", @@ -251,9 +253,9 @@ } }, "node_modules/@aws-amplify/storage": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/@aws-amplify/storage/-/storage-6.7.1.tgz", - "integrity": "sha512-pl8B7M+H1Hg0ALcYLmB0nM/WNBdT0INc+ljPKfIUoxgnzF3jn+uI6xVw6VNN5mmQ6QuDGBOE7wOzWNjyFlfo1Q==", + "version": "6.7.3", + "resolved": "https://registry.npmjs.org/@aws-amplify/storage/-/storage-6.7.3.tgz", + "integrity": "sha512-ow3eJ6jUBQFGr0G3h2CNxIpw1EC5AVhbL0AKCj2CpTFX7LHTzjjtmINJM7EQa5CCgydoxsAgEGPHMw31BfEFew==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.398.0", @@ -1407,9 +1409,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", - "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.3.tgz", + "integrity": "sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -1455,13 +1457,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", - "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", + "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.2", - "@babel/types": "^7.26.0", + "@babel/parser": "^7.26.3", + "@babel/types": "^7.26.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -1575,12 +1577,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", - "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", + "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", "license": "MIT", "dependencies": { - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.3" }, "bin": { "parser": "bin/babel-parser.js" @@ -1646,16 +1648,16 @@ } }, "node_modules/@babel/traverse": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", - "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "version": "7.26.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", + "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/parser": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.3", + "@babel/parser": "^7.26.3", "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9", + "@babel/types": "^7.26.3", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1664,9 +1666,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", + "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -1690,9 +1692,9 @@ "license": "MIT" }, "node_modules/@carbon/charts": { - "version": "1.22.5", - "resolved": "https://registry.npmjs.org/@carbon/charts/-/charts-1.22.5.tgz", - "integrity": "sha512-/EZBnFbud3IOPcCroBgR7mNXOB3U/z0BETyjv38FlkC/f5GtGvnXu+8RtrsYH+YW5JEfZlycjY5F8C5eP6E0Yg==", + "version": "1.22.7", + "resolved": "https://registry.npmjs.org/@carbon/charts/-/charts-1.22.7.tgz", + "integrity": "sha512-xyxbSzmyf1xIPZObmkUkFFU+wNfPqKfRa5isNu94oJgcTlFLw6+TirUIUCgdrONVryxolbvB9KiMs4TbWMISSQ==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -1701,7 +1703,6 @@ "@ibm/telemetry-js": "^1.8.0", "@types/d3": "^7.4.3", "@types/topojson": "^3.2.6", - "carbon-components": "^10.59.0", "d3": "^7.9.0", "d3-cloud": "^1.2.7", "d3-sankey": "^0.12.3", @@ -1714,13 +1715,13 @@ } }, "node_modules/@carbon/charts-react": { - "version": "1.22.5", - "resolved": "https://registry.npmjs.org/@carbon/charts-react/-/charts-react-1.22.5.tgz", - "integrity": "sha512-a/aZiCdN6BSw/b3dbRW4Segfy5bLISDjODdUi+oHoXPHQh8A+Sz9GMN5SREbuZ03jtZUvtocr51dEuoq5y8MKQ==", + "version": "1.22.7", + "resolved": "https://registry.npmjs.org/@carbon/charts-react/-/charts-react-1.22.7.tgz", + "integrity": "sha512-oSp1nCRgJDOlB9Yf0ZnGHoDrayE8ubtzsz6LnPpZ/ibfg4G+LfPHOBOFUP8wX63aHt3FGz7/lbQmg3vqH+7G/g==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@carbon/charts": "1.22.5", + "@carbon/charts": "1.22.7", "@carbon/icons-react": "^11.53.0", "@ibm/telemetry-js": "^1.8.0" }, @@ -1821,9 +1822,9 @@ } }, "node_modules/@carbon/react": { - "version": "1.71.1", - "resolved": "https://registry.npmjs.org/@carbon/react/-/react-1.71.1.tgz", - "integrity": "sha512-wLlHFwJKoCFt2ltpvc9mRfdMtRg3YTNr/GHGcNFNfPjRtNFTuTx/ImmXc9WInctsEts3i2VOizT/pdRJ9fAP+A==", + "version": "1.72.0", + "resolved": "https://registry.npmjs.org/@carbon/react/-/react-1.72.0.tgz", + "integrity": "sha512-cQdf7EDeu7E4fTjP/vqfni4buc8V7XHw2YIlGVeRlLXVSc3WdoJgimLYaKUV4o0vvoqQvmiDEKDu0XdT7USJiw==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -1831,7 +1832,7 @@ "@carbon/feature-flags": "^0.24.0", "@carbon/icons-react": "^11.53.0", "@carbon/layout": "^11.28.0", - "@carbon/styles": "^1.70.0", + "@carbon/styles": "^1.71.0", "@floating-ui/react": "^0.26.0", "@ibm/telemetry-js": "^1.5.0", "classnames": "2.5.1", @@ -1856,9 +1857,9 @@ } }, "node_modules/@carbon/styles": { - "version": "1.70.0", - "resolved": "https://registry.npmjs.org/@carbon/styles/-/styles-1.70.0.tgz", - "integrity": "sha512-iL3dQHufX/2mYhTu7GUbUF3MMn3fua3CaQ4oKSdVGtW2J3+eeZ7m90WUGjgnp/IevVJj+RY06am5k+5l0m2/yg==", + "version": "1.71.0", + "resolved": "https://registry.npmjs.org/@carbon/styles/-/styles-1.71.0.tgz", + "integrity": "sha512-tkQ/Ub7QYHCyFqXJMCe7+Dbpypx7pCefJCeEEluEqpeVSfLu1qtRMZUftfndvzChIZUXtm+ImpHtRknRnyS3+g==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -1889,15 +1890,6 @@ } } }, - "node_modules/@carbon/telemetry": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@carbon/telemetry/-/telemetry-0.1.0.tgz", - "integrity": "sha512-kNWt0bkgPwGW0i5h7HFuljbKRXPvIhsKbB+1tEURAYLXoJg9iJLF1eGvWN5iVoFCS2zje4GR3OGOsvvKVe7Hlg==", - "license": "Apache-2.0", - "bin": { - "carbon-telemetry": "bin/carbon-telemetry.js" - } - }, "node_modules/@carbon/themes": { "version": "11.43.0", "resolved": "https://registry.npmjs.org/@carbon/themes/-/themes-11.43.0.tgz", @@ -3251,9 +3243,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.28.0.tgz", - "integrity": "sha512-wLJuPLT6grGZsy34g4N1yRfYeouklTgPhH1gWXCYspenKYD0s3cR99ZevOGw5BexMNywkbV3UkjADisozBmpPQ==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.28.1.tgz", + "integrity": "sha512-2aZp8AES04KI2dy3Ss6/MDjXbwBzj+i0GqKtWXgw2/Ma6E4jJvujryO6gJAghIRVz7Vwr9Gtl/8na3nDUKpraQ==", "cpu": [ "arm" ], @@ -3264,9 +3256,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.28.0.tgz", - "integrity": "sha512-eiNkznlo0dLmVG/6wf+Ifi/v78G4d4QxRhuUl+s8EWZpDewgk7PX3ZyECUXU0Zq/Ca+8nU8cQpNC4Xgn2gFNDA==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.28.1.tgz", + "integrity": "sha512-EbkK285O+1YMrg57xVA+Dp0tDBRB93/BZKph9XhMjezf6F4TpYjaUSuPt5J0fZXlSag0LmZAsTmdGGqPp4pQFA==", "cpu": [ "arm64" ], @@ -3277,9 +3269,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.28.0.tgz", - "integrity": "sha512-lmKx9yHsppblnLQZOGxdO66gT77bvdBtr/0P+TPOseowE7D9AJoBw8ZDULRasXRWf1Z86/gcOdpBrV6VDUY36Q==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.28.1.tgz", + "integrity": "sha512-prduvrMKU6NzMq6nxzQw445zXgaDBbMQvmKSJaxpaZ5R1QDM8w+eGxo6Y/jhT/cLoCvnZI42oEqf9KQNYz1fqQ==", "cpu": [ "arm64" ], @@ -3290,9 +3282,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.28.0.tgz", - "integrity": "sha512-8hxgfReVs7k9Js1uAIhS6zq3I+wKQETInnWQtgzt8JfGx51R1N6DRVy3F4o0lQwumbErRz52YqwjfvuwRxGv1w==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.28.1.tgz", + "integrity": "sha512-WsvbOunsUk0wccO/TV4o7IKgloJ942hVFK1CLatwv6TJspcCZb9umQkPdvB7FihmdxgaKR5JyxDjWpCOp4uZlQ==", "cpu": [ "x64" ], @@ -3303,9 +3295,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.28.0.tgz", - "integrity": "sha512-lA1zZB3bFx5oxu9fYud4+g1mt+lYXCoch0M0V/xhqLoGatbzVse0wlSQ1UYOWKpuSu3gyN4qEc0Dxf/DII1bhQ==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.28.1.tgz", + "integrity": "sha512-HTDPdY1caUcU4qK23FeeGxCdJF64cKkqajU0iBnTVxS8F7H/7BewvYoG+va1KPSL63kQ1PGNyiwKOfReavzvNA==", "cpu": [ "arm64" ], @@ -3316,9 +3308,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.28.0.tgz", - "integrity": "sha512-aI2plavbUDjCQB/sRbeUZWX9qp12GfYkYSJOrdYTL/C5D53bsE2/nBPuoiJKoWp5SN78v2Vr8ZPnB+/VbQ2pFA==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.28.1.tgz", + "integrity": "sha512-m/uYasxkUevcFTeRSM9TeLyPe2QDuqtjkeoTpP9SW0XxUWfcYrGDMkO/m2tTw+4NMAF9P2fU3Mw4ahNvo7QmsQ==", "cpu": [ "x64" ], @@ -3329,9 +3321,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.28.0.tgz", - "integrity": "sha512-WXveUPKtfqtaNvpf0iOb0M6xC64GzUX/OowbqfiCSXTdi/jLlOmH0Ba94/OkiY2yTGTwteo4/dsHRfh5bDCZ+w==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.28.1.tgz", + "integrity": "sha512-QAg11ZIt6mcmzpNE6JZBpKfJaKkqTm1A9+y9O+frdZJEuhQxiugM05gnCWiANHj4RmbgeVJpTdmKRmH/a+0QbA==", "cpu": [ "arm" ], @@ -3342,9 +3334,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.28.0.tgz", - "integrity": "sha512-yLc3O2NtOQR67lI79zsSc7lk31xjwcaocvdD1twL64PK1yNaIqCeWI9L5B4MFPAVGEVjH5k1oWSGuYX1Wutxpg==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.28.1.tgz", + "integrity": "sha512-dRP9PEBfolq1dmMcFqbEPSd9VlRuVWEGSmbxVEfiq2cs2jlZAl0YNxFzAQS2OrQmsLBLAATDMb3Z6MFv5vOcXg==", "cpu": [ "arm" ], @@ -3355,9 +3347,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.28.0.tgz", - "integrity": "sha512-+P9G9hjEpHucHRXqesY+3X9hD2wh0iNnJXX/QhS/J5vTdG6VhNYMxJ2rJkQOxRUd17u5mbMLHM7yWGZdAASfcg==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.28.1.tgz", + "integrity": "sha512-uGr8khxO+CKT4XU8ZUH1TTEUtlktK6Kgtv0+6bIFSeiSlnGJHG1tSFSjm41uQ9sAO/5ULx9mWOz70jYLyv1QkA==", "cpu": [ "arm64" ], @@ -3368,9 +3360,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.28.0.tgz", - "integrity": "sha512-1xsm2rCKSTpKzi5/ypT5wfc+4bOGa/9yI/eaOLW0oMs7qpC542APWhl4A37AENGZ6St6GBMWhCCMM6tXgTIplw==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.28.1.tgz", + "integrity": "sha512-QF54q8MYGAqMLrX2t7tNpi01nvq5RI59UBNx+3+37zoKX5KViPo/gk2QLhsuqok05sSCRluj0D00LzCwBikb0A==", "cpu": [ "arm64" ], @@ -3380,10 +3372,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.28.1.tgz", + "integrity": "sha512-vPul4uodvWvLhRco2w0GcyZcdyBfpfDRgNKU+p35AWEbJ/HPs1tOUrkSueVbBS0RQHAf/A+nNtDpvw95PeVKOA==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.28.0.tgz", - "integrity": "sha512-zgWxMq8neVQeXL+ouSf6S7DoNeo6EPgi1eeqHXVKQxqPy1B2NvTbaOUWPn/7CfMKL7xvhV0/+fq/Z/J69g1WAQ==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.28.1.tgz", + "integrity": "sha512-pTnTdBuC2+pt1Rmm2SV7JWRqzhYpEILML4PKODqLz+C7Ou2apEV52h19CR7es+u04KlqplggmN9sqZlekg3R1A==", "cpu": [ "ppc64" ], @@ -3394,9 +3399,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.28.0.tgz", - "integrity": "sha512-VEdVYacLniRxbRJLNtzwGt5vwS0ycYshofI7cWAfj7Vg5asqj+pt+Q6x4n+AONSZW/kVm+5nklde0qs2EUwU2g==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.28.1.tgz", + "integrity": "sha512-vWXy1Nfg7TPBSuAncfInmAI/WZDd5vOklyLJDdIRKABcZWojNDY0NJwruY2AcnCLnRJKSaBgf/GiJfauu8cQZA==", "cpu": [ "riscv64" ], @@ -3407,9 +3412,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.28.0.tgz", - "integrity": "sha512-LQlP5t2hcDJh8HV8RELD9/xlYtEzJkm/aWGsauvdO2ulfl3QYRjqrKW+mGAIWP5kdNCBheqqqYIGElSRCaXfpw==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.28.1.tgz", + "integrity": "sha512-/yqC2Y53oZjb0yz8PVuGOQQNOTwxcizudunl/tFs1aLvObTclTwZ0JhXF2XcPT/zuaymemCDSuuUPXJJyqeDOg==", "cpu": [ "s390x" ], @@ -3420,9 +3425,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.28.0.tgz", - "integrity": "sha512-Nl4KIzteVEKE9BdAvYoTkW19pa7LR/RBrT6F1dJCV/3pbjwDcaOq+edkP0LXuJ9kflW/xOK414X78r+K84+msw==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.28.1.tgz", + "integrity": "sha512-fzgeABz7rrAlKYB0y2kSEiURrI0691CSL0+KXwKwhxvj92VULEDQLpBYLHpF49MSiPG4sq5CK3qHMnb9tlCjBw==", "cpu": [ "x64" ], @@ -3433,9 +3438,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.28.0.tgz", - "integrity": "sha512-eKpJr4vBDOi4goT75MvW+0dXcNUqisK4jvibY9vDdlgLx+yekxSm55StsHbxUsRxSTt3JEQvlr3cGDkzcSP8bw==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.28.1.tgz", + "integrity": "sha512-xQTDVzSGiMlSshpJCtudbWyRfLaNiVPXt1WgdWTwWz9n0U12cI2ZVtWe/Jgwyv/6wjL7b66uu61Vg0POWVfz4g==", "cpu": [ "x64" ], @@ -3446,9 +3451,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.28.0.tgz", - "integrity": "sha512-Vi+WR62xWGsE/Oj+mD0FNAPY2MEox3cfyG0zLpotZdehPFXwz6lypkGs5y38Jd/NVSbOD02aVad6q6QYF7i8Bg==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.28.1.tgz", + "integrity": "sha512-wSXmDRVupJstFP7elGMgv+2HqXelQhuNf+IS4V+nUpNVi/GUiBgDmfwD0UGN3pcAnWsgKG3I52wMOBnk1VHr/A==", "cpu": [ "arm64" ], @@ -3459,9 +3464,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.28.0.tgz", - "integrity": "sha512-kN/Vpip8emMLn/eOza+4JwqDZBL6MPNpkdaEsgUtW1NYN3DZvZqSQrbKzJcTL6hd8YNmFTn7XGWMwccOcJBL0A==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.28.1.tgz", + "integrity": "sha512-ZkyTJ/9vkgrE/Rk9vhMXhf8l9D+eAhbAVbsGsXKy2ohmJaWg0LPQLnIxRdRp/bKyr8tXuPlXhIoGlEB5XpJnGA==", "cpu": [ "ia32" ], @@ -3472,9 +3477,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.28.0.tgz", - "integrity": "sha512-Bvno2/aZT6usSa7lRDL2+hMjVAGjuqaymF1ApZm31JXzniR/hvr14jpU+/z4X6Gt5BPlzosscyJZGUvguXIqeQ==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.28.1.tgz", + "integrity": "sha512-ZvK2jBafvttJjoIdKm/Q/Bh7IJ1Ose9IBOwpOXcOvW3ikGTQGmKDgxTC6oCAzW6PynbkKP8+um1du81XJHZ0JA==", "cpu": [ "x64" ], @@ -4508,9 +4513,9 @@ } }, "node_modules/@swc/core": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.9.3.tgz", - "integrity": "sha512-oRj0AFePUhtatX+BscVhnzaAmWjpfAeySpM1TCbxA1rtBDeH/JDhi5yYzAKneDYtVtBvA7ApfeuzhMC9ye4xSg==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.10.1.tgz", + "integrity": "sha512-rQ4dS6GAdmtzKiCRt3LFVxl37FaY1cgL9kSUTnhQ2xc3fmHOd7jdJK/V4pSZMG1ruGTd0bsi34O2R0Olg9Zo/w==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -4525,16 +4530,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.9.3", - "@swc/core-darwin-x64": "1.9.3", - "@swc/core-linux-arm-gnueabihf": "1.9.3", - "@swc/core-linux-arm64-gnu": "1.9.3", - "@swc/core-linux-arm64-musl": "1.9.3", - "@swc/core-linux-x64-gnu": "1.9.3", - "@swc/core-linux-x64-musl": "1.9.3", - "@swc/core-win32-arm64-msvc": "1.9.3", - "@swc/core-win32-ia32-msvc": "1.9.3", - "@swc/core-win32-x64-msvc": "1.9.3" + "@swc/core-darwin-arm64": "1.10.1", + "@swc/core-darwin-x64": "1.10.1", + "@swc/core-linux-arm-gnueabihf": "1.10.1", + "@swc/core-linux-arm64-gnu": "1.10.1", + "@swc/core-linux-arm64-musl": "1.10.1", + "@swc/core-linux-x64-gnu": "1.10.1", + "@swc/core-linux-x64-musl": "1.10.1", + "@swc/core-win32-arm64-msvc": "1.10.1", + "@swc/core-win32-ia32-msvc": "1.10.1", + "@swc/core-win32-x64-msvc": "1.10.1" }, "peerDependencies": { "@swc/helpers": "*" @@ -4546,9 +4551,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.9.3.tgz", - "integrity": "sha512-hGfl/KTic/QY4tB9DkTbNuxy5cV4IeejpPD4zo+Lzt4iLlDWIeANL4Fkg67FiVceNJboqg48CUX+APhDHO5G1w==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.10.1.tgz", + "integrity": "sha512-NyELPp8EsVZtxH/mEqvzSyWpfPJ1lugpTQcSlMduZLj1EASLO4sC8wt8hmL1aizRlsbjCX+r0PyL+l0xQ64/6Q==", "cpu": [ "arm64" ], @@ -4562,9 +4567,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.9.3.tgz", - "integrity": "sha512-IaRq05ZLdtgF5h9CzlcgaNHyg4VXuiStnOFpfNEMuI5fm5afP2S0FHq8WdakUz5WppsbddTdplL+vpeApt/WCQ==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.10.1.tgz", + "integrity": "sha512-L4BNt1fdQ5ZZhAk5qoDfUnXRabDOXKnXBxMDJ+PWLSxOGBbWE6aJTnu4zbGjJvtot0KM46m2LPAPY8ttknqaZA==", "cpu": [ "x64" ], @@ -4578,9 +4583,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.9.3.tgz", - "integrity": "sha512-Pbwe7xYprj/nEnZrNBvZfjnTxlBIcfApAGdz2EROhjpPj+FBqBa3wOogqbsuGGBdCphf8S+KPprL1z+oDWkmSQ==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.10.1.tgz", + "integrity": "sha512-Y1u9OqCHgvVp2tYQAJ7hcU9qO5brDMIrA5R31rwWQIAKDkJKtv3IlTHF0hrbWk1wPR0ZdngkQSJZple7G+Grvw==", "cpu": [ "arm" ], @@ -4594,9 +4599,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.9.3.tgz", - "integrity": "sha512-AQ5JZiwNGVV/2K2TVulg0mw/3LYfqpjZO6jDPtR2evNbk9Yt57YsVzS+3vHSlUBQDRV9/jqMuZYVU3P13xrk+g==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.10.1.tgz", + "integrity": "sha512-tNQHO/UKdtnqjc7o04iRXng1wTUXPgVd8Y6LI4qIbHVoVPwksZydISjMcilKNLKIwOoUQAkxyJ16SlOAeADzhQ==", "cpu": [ "arm64" ], @@ -4610,9 +4615,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.9.3.tgz", - "integrity": "sha512-tzVH480RY6RbMl/QRgh5HK3zn1ZTFsThuxDGo6Iuk1MdwIbdFYUY034heWUTI4u3Db97ArKh0hNL0xhO3+PZdg==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.10.1.tgz", + "integrity": "sha512-x0L2Pd9weQ6n8dI1z1Isq00VHFvpBClwQJvrt3NHzmR+1wCT/gcYl1tp9P5xHh3ldM8Cn4UjWCw+7PaUgg8FcQ==", "cpu": [ "arm64" ], @@ -4626,9 +4631,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.9.3.tgz", - "integrity": "sha512-ivXXBRDXDc9k4cdv10R21ccBmGebVOwKXT/UdH1PhxUn9m/h8erAWjz5pcELwjiMf27WokqPgaWVfaclDbgE+w==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.10.1.tgz", + "integrity": "sha512-yyYEwQcObV3AUsC79rSzN9z6kiWxKAVJ6Ntwq2N9YoZqSPYph+4/Am5fM1xEQYf/kb99csj0FgOelomJSobxQA==", "cpu": [ "x64" ], @@ -4642,9 +4647,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.9.3.tgz", - "integrity": "sha512-ILsGMgfnOz1HwdDz+ZgEuomIwkP1PHT6maigZxaCIuC6OPEhKE8uYna22uU63XvYcLQvZYDzpR3ms47WQPuNEg==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.10.1.tgz", + "integrity": "sha512-tcaS43Ydd7Fk7sW5ROpaf2Kq1zR+sI5K0RM+0qYLYYurvsJruj3GhBCaiN3gkzd8m/8wkqNqtVklWaQYSDsyqA==", "cpu": [ "x64" ], @@ -4658,9 +4663,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.9.3.tgz", - "integrity": "sha512-e+XmltDVIHieUnNJHtspn6B+PCcFOMYXNJB1GqoCcyinkEIQNwC8KtWgMqUucUbEWJkPc35NHy9k8aCXRmw9Kg==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.10.1.tgz", + "integrity": "sha512-D3Qo1voA7AkbOzQ2UGuKNHfYGKL6eejN8VWOoQYtGHHQi1p5KK/Q7V1ku55oxXBsj79Ny5FRMqiRJpVGad7bjQ==", "cpu": [ "arm64" ], @@ -4674,9 +4679,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.9.3.tgz", - "integrity": "sha512-rqpzNfpAooSL4UfQnHhkW8aL+oyjqJniDP0qwZfGnjDoJSbtPysHg2LpcOBEdSnEH+uIZq6J96qf0ZFD8AGfXA==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.10.1.tgz", + "integrity": "sha512-WalYdFoU3454Og+sDKHM1MrjvxUGwA2oralknXkXL8S0I/8RkWZOB++p3pLaGbTvOO++T+6znFbQdR8KRaa7DA==", "cpu": [ "ia32" ], @@ -4690,9 +4695,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.9.3.tgz", - "integrity": "sha512-3YJJLQ5suIEHEKc1GHtqVq475guiyqisKSoUnoaRtxkDaW5g1yvPt9IoSLOe2mRs7+FFhGGU693RsBUSwOXSdQ==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.10.1.tgz", + "integrity": "sha512-JWobfQDbTnoqaIwPKQ3DVSywihVXlQMbDuwik/dDWlj33A8oEHcjPOGs4OqcA3RHv24i+lfCQpM3Mn4FAMfacA==", "cpu": [ "x64" ], @@ -4721,9 +4726,9 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.62.0.tgz", - "integrity": "sha512-sx38bGrqF9bop92AXOvzDr0L9fWDas5zXdPglxa9cuqeVSWS7lY6OnVyl/oodfXjgOGRk79IfCpgVmxrbHuFHg==", + "version": "5.62.3", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.62.3.tgz", + "integrity": "sha512-Jp/nYoz8cnO7kqhOlSv8ke/0MJRJVGuZ0P/JO9KQ+f45mpN90hrerzavyTKeSoT/pOzeoOUkv1Xd0wPsxAWXfg==", "license": "MIT", "funding": { "type": "github", @@ -4731,12 +4736,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.62.0.tgz", - "integrity": "sha512-tj2ltjAn2a3fs+Dqonlvs6GyLQ/LKVJE2DVSYW+8pJ3P6/VCVGrfqv5UEchmlP7tLOvvtZcOuSyI2ooVlR5Yqw==", + "version": "5.62.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.62.3.tgz", + "integrity": "sha512-y2zDNKuhgiuMgsKkqd4AcsLIBiCfEO8U11AdrtAUihmLbRNztPrlcZqx2lH1GacZsx+y1qRRbCcJLYTtF1vKsw==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.62.0" + "@tanstack/query-core": "5.62.3" }, "funding": { "type": "github", @@ -4826,9 +4831,9 @@ "license": "MIT" }, "node_modules/@testing-library/react": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.0.1.tgz", - "integrity": "sha512-dSmwJVtJXmku+iocRhWOUFbrERC76TX2Mnf0ATODz8brzAZrMBbzLwQixlBSanZxR6LddK3eiwpSFZgDET1URg==", + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.1.0.tgz", + "integrity": "sha512-Q2ToPvg0KsVL0ohND9A3zLJWcOXXcO8IDu3fj11KhNt0UlCWyFyvnCIBkd12tidB2lkiVRG8VFqdhcqhqnAQtg==", "dev": true, "license": "MIT", "dependencies": { @@ -4839,10 +4844,10 @@ }, "peerDependencies": { "@testing-library/dom": "^10.0.0", - "@types/react": "^18.0.0", - "@types/react-dom": "^18.0.0", - "react": "^18.0.0", - "react-dom": "^18.0.0" + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -5187,9 +5192,9 @@ "license": "MIT" }, "node_modules/@types/geojson": { - "version": "7946.0.14", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", - "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==", + "version": "7946.0.15", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.15.tgz", + "integrity": "sha512-9oSxFzDCT2Rj6DfcHF8G++jxBKS7mBqXl5xrRW+Kbvjry6Uduya2iiwqHPhVXpasAVMBYKkEPGgKhd3+/HZ6xA==", "license": "MIT" }, "node_modules/@types/istanbul-lib-coverage": { @@ -5283,9 +5288,9 @@ } }, "node_modules/@types/leaflet": { - "version": "1.9.14", - "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.14.tgz", - "integrity": "sha512-sx2q6MDJaajwhKeVgPSvqXd8rhNJSTA3tMidQGduZn9S6WBYxDkCpSpV5xXEmSg7Cgdk/5vJGhVF1kMYLzauBg==", + "version": "1.9.15", + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.15.tgz", + "integrity": "sha512-7UuggAuAs+mva66gtf2OTB1nEhzU/9JED93TIaOEgvFMvG/dIGQaukHE7izHo1Zd+Ko1L4ETUw7TBc8yUxevpg==", "dev": true, "license": "MIT", "dependencies": { @@ -5319,9 +5324,9 @@ } }, "node_modules/@types/prop-types": { - "version": "15.7.13", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", - "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", "dev": true, "license": "MIT" }, @@ -5340,9 +5345,9 @@ "optional": true }, "node_modules/@types/react": { - "version": "18.3.12", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz", - "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", + "version": "18.3.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.14.tgz", + "integrity": "sha512-NzahNKvjNhVjuPBQ+2G7WlxstQ+47kXZNHlUvFakDViuIEfGY926GqhMueQFZ7woG+sPiQKlF36XfrIUVSUfFg==", "dev": true, "license": "MIT", "dependencies": { @@ -5351,13 +5356,13 @@ } }, "node_modules/@types/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", + "version": "18.3.2", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.2.tgz", + "integrity": "sha512-Fqp+rcvem9wEnGr3RY8dYNvSQ8PoLqjZ9HLgaPUOjJJD120uDyOxOjc/39M4Kddp9JQCxpGQbnhVQF0C0ncYVg==", "dev": true, "license": "MIT", "dependencies": { - "@types/react": "*" + "@types/react": "^18" } }, "node_modules/@types/semver": { @@ -5374,6 +5379,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/testing-library__jest-dom": { + "version": "5.14.9", + "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz", + "integrity": "sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/jest": "*" + } + }, "node_modules/@types/topojson": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/@types/topojson/-/topojson-3.2.6.tgz", @@ -5655,9 +5670,9 @@ } }, "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.1.tgz", + "integrity": "sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==", "dev": true, "license": "ISC" }, @@ -5693,9 +5708,9 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.7.tgz", - "integrity": "sha512-deQ4J+yu6nEjmEfcBndbgrRM95IZoRpV1dDVRbZhjUcgYVZz/Wc4YaLiDDt9Sy5qcikrJUZMlrUxDy7dBojebg==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.8.tgz", + "integrity": "sha512-2Y7BPlKH18mAZYAW1tYByudlCYrQyl5RGvnnDYJKW5tCiO5qg3KSAy3XAxcxKz900a0ZXxWtKrMuZLe3lKBpJw==", "dev": true, "license": "MIT", "dependencies": { @@ -5716,8 +5731,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "2.1.7", - "vitest": "2.1.7" + "@vitest/browser": "2.1.8", + "vitest": "2.1.8" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -5726,14 +5741,14 @@ } }, "node_modules/@vitest/expect": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.7.tgz", - "integrity": "sha512-folWk4qQDEedgUyvaZw94LIJuNLoDtY+rhKhhNy0csdwifn/pQz8EWVRnyrW3j0wMpy+xwJT8WiwiYxk+i+s7w==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.8.tgz", + "integrity": "sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.7", - "@vitest/utils": "2.1.7", + "@vitest/spy": "2.1.8", + "@vitest/utils": "2.1.8", "chai": "^5.1.2", "tinyrainbow": "^1.2.0" }, @@ -5741,47 +5756,10 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/mocker": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.7.tgz", - "integrity": "sha512-nKMTnuJrarFH+7llWxeLmYRldIwTY3OM1DzdytHj0f2+fah6Cyk4XbswhjOiTCnAvXsZAEoo1OaD6rneSSU+3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "2.1.7", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.12" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^5.0.0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/@vitest/mocker/node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, "node_modules/@vitest/pretty-format": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.7.tgz", - "integrity": "sha512-HoqRIyfQlXPrRDB43h0lC8eHPUDPwFweMaD6t+psOvwClCC+oZZim6wPMjuoMnRdiFxXqbybg/QbuewgTwK1vA==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.8.tgz", + "integrity": "sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5792,13 +5770,13 @@ } }, "node_modules/@vitest/runner": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.7.tgz", - "integrity": "sha512-MrDNpXUIXksR57qipYh068SOX4N1hVw6oVILlTlfeTyA1rp0asuljyp15IZwKqhjpWLObFj+tiNrOM4R8UnSqg==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.8.tgz", + "integrity": "sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "2.1.7", + "@vitest/utils": "2.1.8", "pathe": "^1.1.2" }, "funding": { @@ -5806,13 +5784,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.7.tgz", - "integrity": "sha512-OioIxV/xS393DKdlkRNhmtY0K37qVdCv8w1M2SlLTBSX+fNK6zgcd01VlT1nXdbKVDaB8Zb6BOfQYYoGeGTEGg==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.8.tgz", + "integrity": "sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.7", + "@vitest/pretty-format": "2.1.8", "magic-string": "^0.30.12", "pathe": "^1.1.2" }, @@ -5821,9 +5799,9 @@ } }, "node_modules/@vitest/spy": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.7.tgz", - "integrity": "sha512-e5pzIaIC0LBrb/j1FaF7HXlPJLGtltiAkwXTMqNEHALJc7USSLEwziJ+aIWTmjsWNg89zazg37h7oZITnublsQ==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.8.tgz", + "integrity": "sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==", "dev": true, "license": "MIT", "dependencies": { @@ -5834,13 +5812,13 @@ } }, "node_modules/@vitest/utils": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.7.tgz", - "integrity": "sha512-7gUdvIzCCuIrMZu0WHTvDJo8C1NsUtOqmwmcS3bRHUcfHemj29wmkzLVNuWQD7WHoBD/+I7WIgrnzt7kxR54ow==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.8.tgz", + "integrity": "sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.7", + "@vitest/pretty-format": "2.1.8", "loupe": "^3.1.2", "tinyrainbow": "^1.2.0" }, @@ -5872,14 +5850,11 @@ } }, "node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", "dev": true, "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, "engines": { "node": ">= 14" } @@ -6173,18 +6148,18 @@ } }, "node_modules/aws-amplify": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/aws-amplify/-/aws-amplify-6.10.0.tgz", - "integrity": "sha512-QtkGvdxlqjO1LQXrxCq1N9AQ6RVKtcb2PkNXmQbkbpjEyQzLv5snq/oakwIwcy3MaltjTjZf7VfFP5cB4N+y8w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-amplify/analytics": "7.0.60", - "@aws-amplify/api": "6.1.5", - "@aws-amplify/auth": "6.8.0", - "@aws-amplify/core": "6.7.0", - "@aws-amplify/datastore": "5.0.62", - "@aws-amplify/notifications": "2.0.60", - "@aws-amplify/storage": "6.7.1", + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/aws-amplify/-/aws-amplify-6.10.2.tgz", + "integrity": "sha512-B0qXYk6BEXUsm23O4cLbRoeW1Rajsjpf1k88p26/LbS8Ccq4c2ZzpU0toV+I1EOuRgTakpJXIGF2VAtnYRRMDw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/analytics": "7.0.62", + "@aws-amplify/api": "6.1.7", + "@aws-amplify/auth": "6.8.2", + "@aws-amplify/core": "6.7.2", + "@aws-amplify/datastore": "5.0.64", + "@aws-amplify/notifications": "2.0.62", + "@aws-amplify/storage": "6.7.3", "tslib": "^2.5.0" } }, @@ -6199,9 +6174,9 @@ } }, "node_modules/axios": { - "version": "1.7.8", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.8.tgz", - "integrity": "sha512-Uu0wb7KNqK2t5K+YQyVCLM76prD5sRFjKHbJYCP1J7JFGEQ6nN7HWn9+04LAeiJ3ji54lgS/gZCH1oxyrf1SPw==", + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -6374,16 +6349,15 @@ } }, "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "set-function-length": "^1.2.2" }, "engines": { "node": ">= 0.4" @@ -6392,6 +6366,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -6414,9 +6401,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001685", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001685.tgz", - "integrity": "sha512-e/kJN1EMyHQzgcMEEgoo+YTCO1NGCmIYHk5Qk8jT6AazWemS5QFKJ5ShCJlH3GZrNIdZofcNCEwZqbMjjKzmnA==", + "version": "1.0.30001687", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001687.tgz", + "integrity": "sha512-0S/FDhf4ZiqrTUiQ39dKeUjYRjkv7lOZU1Dgif2rIqrTzX/1wV2hfKu9TOm1IHkdSijfLswxTFzl/cvir+SLSQ==", "funding": [ { "type": "opencollective", @@ -6460,26 +6447,6 @@ "license": "MIT", "optional": true }, - "node_modules/carbon-components": { - "version": "10.59.0", - "resolved": "https://registry.npmjs.org/carbon-components/-/carbon-components-10.59.0.tgz", - "integrity": "sha512-RXCmJqV+BjTku8Q1aVq3omb5gBilQjdmhfmMikxBSNmX0+K+mAWLmYH/L0pHf56t4FnvQZqc+44P5Sj3zB3GrQ==", - "deprecated": "This package is no longer supported. More info at https://carbondesignsystem.com/deprecations/", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@carbon/telemetry": "0.1.0", - "flatpickr": "4.6.1", - "lodash.debounce": "^4.0.8", - "warning": "^3.0.0" - } - }, - "node_modules/carbon-components/node_modules/flatpickr": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.1.tgz", - "integrity": "sha512-3ULSxbXmcMIRzer/2jLNweoqHpwDvsjEawO2FUd9UFR8uPwLM+LruZcPDpuZStcEgbQKhuFOfXo4nYdGladSNw==", - "license": "MIT" - }, "node_modules/chai": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", @@ -7321,9 +7288,9 @@ } }, "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -7481,9 +7448,9 @@ "license": "MIT" }, "node_modules/dompurify": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.2.tgz", - "integrity": "sha512-YMM+erhdZ2nkZ4fTNRTSI94mb7VG7uVF5vj5Zde7tImgnhZE3R6YW/IACGIHb2ux+QkEXMhe591N+5jWOmL4Zw==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.3.tgz", + "integrity": "sha512-U1U5Hzc2MO0oW3DF+G9qYN0aT7atAou4AgI0XjWz061nyBPbdxkfdhfy5uMgGn6+oLFCfn44ZGbdDqCzVmlOWA==", "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" @@ -7521,6 +7488,20 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "license": "MIT" }, + "node_modules/dunder-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.0.tgz", + "integrity": "sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/earcut": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", @@ -7536,9 +7517,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.67", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.67.tgz", - "integrity": "sha512-nz88NNBsD7kQSAGGJyp8hS6xSPtWwqNogA0mjtc2nUYeEf3nURK9qpV18TuBdDmEDgVWotS8Wkzf+V52dSQ/LQ==", + "version": "1.5.71", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.71.tgz", + "integrity": "sha512-dB68l59BI75W1BUGVTAEJy45CEVuEGy9qPVVQ8pnHyHMn36PLPPoE1mjLH+lo9rKulO3HC2OhbACI/8tCqJBcA==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -7637,13 +7618,10 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, "engines": { "node": ">= 0.4" } @@ -8939,16 +8917,19 @@ "peer": true }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.5.tgz", + "integrity": "sha512-Y4+pKa7XeRUPWFNvOOYHkRYrfzW07oraURSvjDmRVOJ748OrVmeXtpE4+GCEHncjCjkTxPNRt8kEbxDhsn6VTg==", "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "dunder-proto": "^1.0.0", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -9121,13 +9102,10 @@ "license": "MIT" }, "node_modules/gopd": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.1.0.tgz", - "integrity": "sha512-FQoVQnqcdk4hVM4JN1eromaun4iuS34oStkdlLENLdpULsuQcTyXj8w7ayhuUfPwEYZ1ZOooOTT6fdA9Vmx/RA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, "engines": { "node": ">= 0.4" }, @@ -9198,12 +9176,13 @@ } }, "node_modules/has-proto": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.1.0.tgz", - "integrity": "sha512-QLdzI9IIO1Jg7f9GT1gXpPpXArAn6cS31R1eEZqz08Gc+uQ8/XiqHWt17Fiw+2p6oTTIq5GXEpQkAlA88YRl/Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7" + "dunder-proto": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -9213,9 +9192,9 @@ } }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -9307,13 +9286,13 @@ } }, "node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { @@ -9512,13 +9491,16 @@ } }, "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, "license": "MIT", "dependencies": { - "has-bigints": "^1.0.1" + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -9819,13 +9801,15 @@ } }, "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.0.tgz", + "integrity": "sha512-qS8KkNNXUZ/I+nX6QT8ZS1/Yx0A444yhzdTKxCzKkNjQ9sHErBxJnJAgh+f5YhusYECEcjo4XcyH87hn6+ks0A==", "dev": true, "license": "MIT", "dependencies": { - "has-symbols": "^1.0.2" + "call-bind": "^1.0.7", + "has-symbols": "^1.0.3", + "safe-regex-test": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -10315,9 +10299,9 @@ } }, "node_modules/jspdf/node_modules/dompurify": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.7.tgz", - "integrity": "sha512-2q4bEI+coQM8f5ez7kt2xclg1XsecaV9ASJk/54vwlfRRNQfDqJz2pzQ8t0Ix/ToBpXlVjrRIx7pFC/o8itG2Q==", + "version": "2.5.8", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.8.tgz", + "integrity": "sha512-o1vSNgrmYMQObbSSvF/1brBYEQPHhV1+gsmrusO7/GXtp1T9rCS8cXFqVxK/9crT1jA6Ccv+5MTSjBNqr7Sovw==", "license": "(MPL-2.0 OR Apache-2.0)", "optional": true }, @@ -11420,9 +11404,9 @@ } }, "node_modules/react-router": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.0.1.tgz", - "integrity": "sha512-WVAhv9oWCNsja5AkK6KLpXJDSJCQizOIyOd4vvB/+eHGbYx5vkhcmcmwWjQ9yqkRClogi+xjEg9fNEOd5EX/tw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.0.2.tgz", + "integrity": "sha512-m5AcPfTRUcjwmhBzOJGEl6Y7+Crqyju0+TgTQxoS4SO+BkWbhOrcfZNq6wSWdl2BBbJbsAoBUb8ZacOFT+/JlA==", "license": "MIT", "dependencies": { "@types/cookie": "^0.6.0", @@ -11444,12 +11428,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.0.1.tgz", - "integrity": "sha512-duBzwAAiIabhFPZfDjcYpJ+f08TMbPMETgq254GWne2NW1ZwRHhZLj7tpSp8KGb7JvZzlLcjGUnqLxpZQVEPng==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.0.2.tgz", + "integrity": "sha512-VJOQ+CDWFDGaWdrG12Nl+d7yHtLaurNgAQZVgaIy7/Xd+DojgmYLosFfZdGz1wpxmjJIAkAMVTKWcvkx1oggAw==", "license": "MIT", "dependencies": { - "react-router": "7.0.1" + "react-router": "7.0.2" }, "engines": { "node": ">=20.0.0" @@ -11487,19 +11471,20 @@ } }, "node_modules/reflect.getprototypeof": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.7.tgz", - "integrity": "sha512-bMvFGIUKlc/eSfXNX+aZ+EL95/EgZzuwA0OBPTbZZDEJw/0AkentjMuM1oiRfwHrshqk4RzdgiTg5CcDalXN5g==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.8.tgz", + "integrity": "sha512-B5dj6usc5dkk8uFliwjwDHM8To5/QwdKz9JcBZ8Ic4G1f0YmeeJTtE/ZTdgRFPAfxZFiUaPhZ1Jcs4qeagItGQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", + "dunder-proto": "^1.0.0", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "which-builtin-type": "^1.1.4" + "gopd": "^1.2.0", + "which-builtin-type": "^1.2.0" }, "engines": { "node": ">= 0.4" @@ -11631,9 +11616,9 @@ "license": "Unlicense" }, "node_modules/rollup": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.28.0.tgz", - "integrity": "sha512-G9GOrmgWHBma4YfCcX8PjH0qhXSdH8B4HDE2o4/jaxj93S4DPCIDoLcXz99eWMji4hB29UFCEd7B2gwGJDR9cQ==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.28.1.tgz", + "integrity": "sha512-61fXYl/qNVinKmGSTHAZ6Yy8I3YIJC/r2m9feHo6SwVAVcLT5MPwOUFe7EuURA/4m0NR8lXG4BBXuo/IZEsjMg==", "license": "MIT", "dependencies": { "@types/estree": "1.0.6" @@ -11646,24 +11631,25 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.28.0", - "@rollup/rollup-android-arm64": "4.28.0", - "@rollup/rollup-darwin-arm64": "4.28.0", - "@rollup/rollup-darwin-x64": "4.28.0", - "@rollup/rollup-freebsd-arm64": "4.28.0", - "@rollup/rollup-freebsd-x64": "4.28.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.28.0", - "@rollup/rollup-linux-arm-musleabihf": "4.28.0", - "@rollup/rollup-linux-arm64-gnu": "4.28.0", - "@rollup/rollup-linux-arm64-musl": "4.28.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.28.0", - "@rollup/rollup-linux-riscv64-gnu": "4.28.0", - "@rollup/rollup-linux-s390x-gnu": "4.28.0", - "@rollup/rollup-linux-x64-gnu": "4.28.0", - "@rollup/rollup-linux-x64-musl": "4.28.0", - "@rollup/rollup-win32-arm64-msvc": "4.28.0", - "@rollup/rollup-win32-ia32-msvc": "4.28.0", - "@rollup/rollup-win32-x64-msvc": "4.28.0", + "@rollup/rollup-android-arm-eabi": "4.28.1", + "@rollup/rollup-android-arm64": "4.28.1", + "@rollup/rollup-darwin-arm64": "4.28.1", + "@rollup/rollup-darwin-x64": "4.28.1", + "@rollup/rollup-freebsd-arm64": "4.28.1", + "@rollup/rollup-freebsd-x64": "4.28.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.28.1", + "@rollup/rollup-linux-arm-musleabihf": "4.28.1", + "@rollup/rollup-linux-arm64-gnu": "4.28.1", + "@rollup/rollup-linux-arm64-musl": "4.28.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.28.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.28.1", + "@rollup/rollup-linux-riscv64-gnu": "4.28.1", + "@rollup/rollup-linux-s390x-gnu": "4.28.1", + "@rollup/rollup-linux-x64-gnu": "4.28.1", + "@rollup/rollup-linux-x64-musl": "4.28.1", + "@rollup/rollup-win32-arm64-msvc": "4.28.1", + "@rollup/rollup-win32-ia32-msvc": "4.28.1", + "@rollup/rollup-win32-x64-msvc": "4.28.1", "fsevents": "~2.3.2" } }, @@ -11764,9 +11750,9 @@ "license": "MIT" }, "node_modules/sass": { - "version": "1.81.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.81.0.tgz", - "integrity": "sha512-Q4fOxRfhmv3sqCLoGfvrC9pRV8btc0UtqL9mN6Yrv6Qi9ScL55CVH1vlPP863ISLEEMNLLuu9P+enCeGHlnzhA==", + "version": "1.82.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.82.0.tgz", + "integrity": "sha512-j4GMCTa8elGyN9A7x7bEglx0VgSpNUG4W4wNedQ33wSMdnkqQCT8HTwOaVSV4e6yQovcu/3Oc4coJP/l0xhL2Q==", "license": "MIT", "dependencies": { "chokidar": "^4.0.0", @@ -11784,9 +11770,9 @@ } }, "node_modules/sass-loader": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.3.tgz", - "integrity": "sha512-gosNorT1RCkuCMyihv6FBRR7BMV06oKRAs+l4UMp1mlcVg9rWN6KMmUj3igjQwmYys4mDP3etEYJgiHRbgHCHA==", + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.4.tgz", + "integrity": "sha512-LavLbgbBGUt3wCiYzhuLLu65+fWXaXLmq7YxivLhEqmiupCFZ5sKUAipK3do6V80YSU0jvSxNhEdT13IXNr3rg==", "dev": true, "license": "MIT", "dependencies": { @@ -12530,22 +12516,22 @@ } }, "node_modules/tldts": { - "version": "6.1.65", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.65.tgz", - "integrity": "sha512-xU9gLTfAGsADQ2PcWee6Hg8RFAv0DnjMGVJmDnUmI8a9+nYmapMQix4afwrdaCtT+AqP4MaxEzu7cCrYmBPbzQ==", + "version": "6.1.66", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.66.tgz", + "integrity": "sha512-l3ciXsYFel/jSRfESbyKYud1nOw7WfhrBEF9I3UiarYk/qEaOOwu3qXNECHw4fHGHGTEOuhf/VdKgoDX5M/dhQ==", "dev": true, "license": "MIT", "dependencies": { - "tldts-core": "^6.1.65" + "tldts-core": "^6.1.66" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "6.1.65", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.65.tgz", - "integrity": "sha512-Uq5t0N0Oj4nQSbU8wFN1YYENvMthvwU13MQrMJRspYCGLSAZjAfoBOJki5IQpnBM/WFskxxC/gIOTwaedmHaSg==", + "version": "6.1.66", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.66.tgz", + "integrity": "sha512-s07jJruSwndD2X8bVjwioPfqpIc1pDTzszPe9pL1Skbh4bjytL85KNQ3tolqLbCvpQHawIsGfFi9dgerWjqW4g==", "dev": true, "license": "MIT" }, @@ -12911,9 +12897,9 @@ } }, "node_modules/vite": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.2.tgz", - "integrity": "sha512-XdQ+VsY2tJpBsKGs0wf3U/+azx8BBpYRHFAyKm5VeEZNOJZRB63q7Sc8Iup3k0TrN3KO6QgyzFf+opSbfY1y0g==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.3.tgz", + "integrity": "sha512-Cmuo5P0ENTN6HxLSo6IHsjCLn/81Vgrp81oaiFFMRa8gGDj5xEjIcEpf2ZymZtZR8oU0P2JX5WuUp/rlXcHkAw==", "license": "MIT", "dependencies": { "esbuild": "^0.24.0", @@ -12982,9 +12968,9 @@ } }, "node_modules/vite-node": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.7.tgz", - "integrity": "sha512-b/5MxSWd0ftWt1B1LHfzCw0ASzaxHztUwP0rcsBhkDSGy9ZDEDieSIjFG3I78nI9dUN0eSeD6LtuKPZGjwwpZQ==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.8.tgz", + "integrity": "sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==", "dev": true, "license": "MIT", "dependencies": { @@ -12998,7 +12984,7 @@ "vite-node": "vite-node.mjs" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^18.0.0 || >=20.0.0" }, "funding": { "url": "https://opencollective.com/vitest" @@ -13509,9 +13495,9 @@ } }, "node_modules/vite-tsconfig-paths": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.3.tgz", - "integrity": "sha512-0bz+PDlLpGfP2CigeSKL9NFTF1KtXkeHGZSSaGQSuPZH77GhoiQaA8IjYgOaynSuwlDTolSUEU0ErVvju3NURg==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz", + "integrity": "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==", "license": "MIT", "dependencies": { "debug": "^4.1.1", @@ -13528,19 +13514,19 @@ } }, "node_modules/vitest": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.7.tgz", - "integrity": "sha512-wzJ7Wri44ufkzTZbI1lHsdHfiGdFRmnJ9qIudDQ6tknjJeHhF5QgNSSjk7KRZUU535qEiEXFJ7tSHqyzyIv0jQ==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.8.tgz", + "integrity": "sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "2.1.7", - "@vitest/mocker": "2.1.7", - "@vitest/pretty-format": "^2.1.7", - "@vitest/runner": "2.1.7", - "@vitest/snapshot": "2.1.7", - "@vitest/spy": "2.1.7", - "@vitest/utils": "2.1.7", + "@vitest/expect": "2.1.8", + "@vitest/mocker": "2.1.8", + "@vitest/pretty-format": "^2.1.8", + "@vitest/runner": "2.1.8", + "@vitest/snapshot": "2.1.8", + "@vitest/spy": "2.1.8", + "@vitest/utils": "2.1.8", "chai": "^5.1.2", "debug": "^4.3.7", "expect-type": "^1.1.0", @@ -13552,23 +13538,23 @@ "tinypool": "^1.0.1", "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "2.1.7", + "vite-node": "2.1.8", "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^18.0.0 || >=20.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "@edge-runtime/vm": "*", - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "2.1.7", - "@vitest/ui": "2.1.7", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "2.1.8", + "@vitest/ui": "2.1.8", "happy-dom": "*", "jsdom": "*" }, @@ -13984,6 +13970,33 @@ "node": ">=12" } }, + "node_modules/vitest/node_modules/@vitest/mocker": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.8.tgz", + "integrity": "sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.8", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, "node_modules/vitest/node_modules/esbuild": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", @@ -14023,6 +14036,16 @@ "@esbuild/win32-x64": "0.21.5" } }, + "node_modules/vitest/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/vitest/node_modules/vite": { "version": "5.4.11", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", @@ -14108,15 +14131,6 @@ "node": ">=18" } }, - "node_modules/warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", - "integrity": "sha512-jMBt6pUrKn5I+OGgtQ4YZLdhIeJmObddh6CsibPxyQ5yPZm1XExSyzC1LCNX7BzhxWgiHmizBWJTHJIjMjTQYQ==", - "license": "BSD-3-Clause", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, "node_modules/web-vitals": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", @@ -14157,9 +14171,9 @@ } }, "node_modules/whatwg-url": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", - "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.0.tgz", + "integrity": "sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==", "dev": true, "license": "MIT", "dependencies": { @@ -14187,17 +14201,20 @@ } }, "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.0.tgz", + "integrity": "sha512-Ei7Miu/AXe2JJ4iNF5j/UphAgRoma4trE6PtisM09bPygb3egMH3YLW/befsWb1A1AxvNSFidOFTB18XtnIIng==", "dev": true, "license": "MIT", "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.0", + "is-number-object": "^1.1.0", + "is-string": "^1.1.0", + "is-symbol": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" diff --git a/frontend/package.json b/frontend/package.json index 00082c76..f4814830 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -5,6 +5,7 @@ "type": "module", "dependencies": { "@bcgov-nr/nr-theme": "^1.7.0", + "@carbon/charts": "^1.22.7", "@carbon/charts-react": "^1.22.5", "@carbon/icons-react": "^11.53.0", "@carbon/pictograms-react": "^11.69.0", @@ -67,6 +68,7 @@ "@types/qs": "^6.9.16", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", + "@types/testing-library__jest-dom": "^5.14.9", "@typescript-eslint/eslint-plugin": "^6.5.0", "@typescript-eslint/parser": "^6.5.0", "@vitest/coverage-v8": "^2.0.0", @@ -83,7 +85,7 @@ "sass": "^1.62.1", "sass-loader": "^16.0.0", "typescript": "^5.2.2", - "vitest": "^2.0.0" + "vitest": "^2.1.8" }, "overrides": { "braces@3.0.2": "3.0.3", diff --git a/frontend/src/__test__/components/BarChartGrouped.test.tsx b/frontend/src/__test__/components/BarChartGrouped.test.tsx index f1d12e98..1c397d29 100644 --- a/frontend/src/__test__/components/BarChartGrouped.test.tsx +++ b/frontend/src/__test__/components/BarChartGrouped.test.tsx @@ -1,40 +1,207 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import BarChartGrouped from '../../components/BarChartGrouped'; -import { useDistrictListQuery, useFetchOpeningsPerYear } from '../../services/queries/dashboard/dashboardQueries'; -import { describe, expect, it } from 'vitest'; -import { vi } from 'vitest'; -import '@testing-library/jest-dom'; -// Mock the hook -vi.mock('../../services/queries/dashboard/dashboardQueries', () => ({ - useFetchOpeningsPerYear: vi.fn(), - useDistrictListQuery: vi.fn(), +// React and test imports +import React from "react"; +import { render, screen, fireEvent, act, waitFor } from "@testing-library/react"; +import { vi } from "vitest"; + +// Third-party library imports +import { BrowserRouter } from "react-router-dom"; + +// Utility functions +import {fetchOpeningsPerYear} from "../../services/OpeningService"; + +// Local components +import BarChartGrouped from "../../components/BarChartGrouped"; + +// Mock services +vi.mock("../../services/OpeningService",async () => { + const actual = await vi.importActual("../../services/OpeningService"); + return { + ...actual, + fetchOpeningsPerYear: vi.fn(), + }; +}); + +vi.mock("../../services/search/openings", () => ({ + fetchOrgUnits: vi.fn(() => Promise.resolve([{ orgUnitCode: "001", orgUnitName: "Unit 1" }])), + status: [ + { value: "open", text: "Open" }, + { value: "closed", text: "Closed" }, + ], })); -const queryClient = new QueryClient(); +// Mock navigator +vi.mock("react-router-dom", async () => { + const actual = await vi.importActual("react-router-dom"); + return { + ...actual, + useNavigate: vi.fn(), + }; +}); + +// Sample data +const sampleResultsApi = [ + { + "month": 12, + "year": 2023, + "monthName": "Dec", + "amount": 0, + "statusCounts": {} + }, + { + "month": 1, + "year": 2024, + "monthName": "Jan", + "amount": 0, + "statusCounts": {} + }, + { + "month": 2, + "year": 2024, + "monthName": "Feb", + "amount": 0, + "statusCounts": {} + }, + { + "month": 3, + "year": 2024, + "monthName": "Mar", + "amount": 0, + "statusCounts": {} + }, + { + "month": 4, + "year": 2024, + "monthName": "Apr", + "amount": 0, + "statusCounts": {} + }, + { + "month": 5, + "year": 2024, + "monthName": "May", + "amount": 0, + "statusCounts": {} + }, + { + "month": 6, + "year": 2024, + "monthName": "Jun", + "amount": 0, + "statusCounts": {} + }, + { + "month": 7, + "year": 2024, + "monthName": "Jul", + "amount": 3, + "statusCounts": { + "APP": 3 + } + }, + { + "month": 8, + "year": 2024, + "monthName": "Aug", + "amount": 0, + "statusCounts": {} + }, + { + "month": 9, + "year": 2024, + "monthName": "Sep", + "amount": 0, + "statusCounts": {} + }, + { + "month": 10, + "year": 2024, + "monthName": "Oct", + "amount": 0, + "statusCounts": {} + }, + { + "month": 11, + "year": 2024, + "monthName": "Nov", + "amount": 0, + "statusCounts": {} + } +]; -describe('BarChartGrouped component', () => { - it('should display loading state when data is fetching', () => { - // Mock loading state for openings data - (useFetchOpeningsPerYear as any).mockReturnValue({ - data: [], - isLoading: true, - }); +const sampleResults = sampleResultsApi.map(item => ({ + group: "Openings", + key: `${item.monthName} ${item.year}`, + year: item.year, + month: item.month, + value: item.amount, + statusCount: item.statusCounts +})); - // If you're using useDistrictListQuery, mock it too - (useDistrictListQuery as any).mockReturnValue({ - data: [], - isLoading: false, - }); +describe("BarChartGrouped Component", () => { + it("renders without crashing", () => { + (fetchOpeningsPerYear as vi.Mock).mockResolvedValue([]); + render( + + + + ); + expect(screen.getByText(/Loading/i)).toBeInTheDocument(); + }); + it("displays filter dropdowns and date pickers", async () => { + (fetchOpeningsPerYear as vi.Mock).mockResolvedValue([]); render( - + - + ); - // Check if loading text is displayed - expect(screen.getByText('Loading...')).toBeInTheDocument(); + // Wait for async fetch calls + await waitFor(() => expect(screen.getByText("District")).toBeInTheDocument()); + expect(screen.getByText("Status")).toBeInTheDocument(); + expect(screen.getByLabelText("Start Date")).toBeInTheDocument(); + expect(screen.getByLabelText("End Date")).toBeInTheDocument(); }); + + it("calls the fetchOrgUnits function on mount", async () => { + const { fetchOrgUnits } = await import("../../services/search/openings"); + (fetchOpeningsPerYear as vi.Mock).mockResolvedValue([]); + render( + + + + ); + + await waitFor(() => expect(fetchOrgUnits).toHaveBeenCalled()); + }); + + it("updates selected filters when a filterable multi-select item is chosen", async () => { + (fetchOpeningsPerYear as vi.Mock).mockResolvedValue([]); + await act(async () =>render( + + + + )); + + await waitFor(() => screen.getByText("District")); + + const dropdown = screen.getByPlaceholderText("Filter by district"); + fireEvent.change(dropdown, { target: { value: "Unit 1" } }); + + expect(screen.getByDisplayValue("Unit 1")).toBeInTheDocument(); + }); + + it("renders empty state when no data is available", async () => { + (fetchOpeningsPerYear as vi.Mock).mockResolvedValue([]); + render( + + + + ); + + await waitFor(() => screen.getByText("You don't have any openings to show yet")); + + expect(screen.getByText("Select a filter to bring up the openings")).toBeInTheDocument(); + }); + }); \ No newline at end of file diff --git a/frontend/src/__test__/components/BarChartTooltip.test.tsx b/frontend/src/__test__/components/BarChartTooltip.test.tsx new file mode 100644 index 00000000..8c2534b3 --- /dev/null +++ b/frontend/src/__test__/components/BarChartTooltip.test.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { describe, it, expect, vi } from 'vitest'; +import BarChartTooltip from '../../components/BarChartTooltip'; +import { OpeningPerYearChart } from '../../types/OpeningPerYearChart'; + +vi.mock('../../services/search/openings', () => ({ + status: [ + { value: 'open', text: 'Open' }, + { value: 'closed', text: 'Closed' }, + ], +})); + +describe('BarChartTooltip', () => { + it('renders the chart tooltip correctly', () => { + const mockDatum: OpeningPerYearChart = { + value: '2023', + group: 'Engineering', + key: 'Devs', + statusCount: { + open: 10, + closed: 5, + }, + }; + + render(); + + // Header with value and group + expect(screen.getByText('2023 Engineering')).toBeInTheDocument(); + + // Status counts and descriptions + expect(screen.getByText('10 open')).toBeInTheDocument(); + expect(screen.getByText('5 closed')).toBeInTheDocument(); + }); + + it('falls back to the status code if description is unavailable', () => { + const mockDatum: OpeningPerYearChart = { + value: '2023', + group: 'HR', + key: 'Recruiters', + statusCount: { + unknown: 3, + }, + }; + + render(); + + expect(screen.getByText('3 unknown')).toBeInTheDocument(); + }); +}); \ No newline at end of file diff --git a/frontend/src/__test__/components/OpeningMetricsTab.test.tsx b/frontend/src/__test__/components/OpeningMetricsTab.test.tsx index b646da02..e441bcb9 100644 --- a/frontend/src/__test__/components/OpeningMetricsTab.test.tsx +++ b/frontend/src/__test__/components/OpeningMetricsTab.test.tsx @@ -1,11 +1,21 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest'; +// React and test imports import React from 'react'; +import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, act, waitFor, screen } from '@testing-library/react'; -import OpeningMetricsTab from '../../components/OpeningMetricsTab'; -import { NotificationProvider } from '../../contexts/NotificationProvider'; + +// Third-party library imports +import { BrowserRouter } from "react-router-dom"; + +// Utility functions import { fetchOpeningFavourites } from '../../services/OpeningFavouriteService'; import { fetchFreeGrowingMilestones, fetchOpeningsPerYear, fetchRecentOpenings, fetchRecentActions } from '../../services/OpeningService'; +// Local components +import OpeningMetricsTab from '../../components/OpeningMetricsTab'; +import { NotificationProvider } from '../../contexts/NotificationProvider'; + + +// Mock services vi.mock('../../services/OpeningFavouriteService', () => ({ fetchOpeningFavourites: vi.fn(), })); @@ -41,7 +51,7 @@ describe('OpeningMetricsTab', () => { it('should render the OpeningMetricsTab component with all sections', async () => { - await act(async () => render()); + await act(async () => render()); expect(screen.getByText('Dashboard')).toBeInTheDocument(); expect(screen.getByText('Manage and track silvicultural information about openings')).toBeInTheDocument(); @@ -57,7 +67,7 @@ describe('OpeningMetricsTab', () => { let container; await act(async () => { - ({ container } = render()); + ({ container } = render()); }); await waitFor(() => { @@ -97,7 +107,7 @@ describe('OpeningMetricsTab', () => { delete window.location; window.location = { search: '?scrollTo=trackOpenings' } as any; - await act(async () => render()); + await act(async () => render()); expect(mockScrollIntoView).toHaveBeenCalledWith({ behavior: 'smooth' }); @@ -113,7 +123,7 @@ describe('OpeningMetricsTab', () => { delete window.location; window.location = { search: '' } as any; - await act(async () => render()); + await act(async () => render()); expect(mockScrollIntoView).not.toHaveBeenCalled(); diff --git a/frontend/src/__test__/screens/SilvicultureSearch.test.tsx b/frontend/src/__test__/screens/SilvicultureSearch.test.tsx new file mode 100644 index 00000000..d2bce721 --- /dev/null +++ b/frontend/src/__test__/screens/SilvicultureSearch.test.tsx @@ -0,0 +1,89 @@ +import React from "react"; +import { render, screen, fireEvent, waitFor } from "@testing-library/react"; +import { describe, it, expect, vi } from "vitest"; +import SilvicultureSearch from "../../screens/SilvicultureSearch"; +import { BrowserRouter } from "react-router-dom"; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { OpeningsSearchProvider } from "../../contexts/search/OpeningsSearch"; + +describe("SilvicultureSearch Component", () => { + const queryClient = new QueryClient(); + + const renderComponent = () => + render( + + + + + + + + ); + + it("should render the page title correctly", () => { + renderComponent(); + + const titleElement = screen.getByText("Silviculture Search"); + const subtitleElement = screen.getByText( + "Search for opening types, activities, stocking standards or standards units" + ); + + expect(titleElement).toBeInTheDocument(); + expect(subtitleElement).toBeInTheDocument(); + }); + + it("should render all tabs with correct names and icons", () => { + renderComponent(); + + const tabs = screen.getAllByRole("tab"); + expect(tabs).toHaveLength(4); + + expect(tabs[0]).toHaveTextContent("Openings"); + expect(tabs[1]).toHaveTextContent("Activities"); + expect(tabs[2]).toHaveTextContent("Stocking standards"); + expect(tabs[3]).toHaveTextContent("Standard units"); + + expect(tabs[1]).toHaveAttribute("aria-disabled", "true"); + expect(tabs[2]).toHaveAttribute("aria-disabled", "true"); + expect(tabs[3]).toHaveAttribute("aria-disabled", "true"); + }); + + it("should switch to the correct tab when a tab is clicked", async () => { + renderComponent(); + + const tabs = screen.getAllByRole("tab"); + + // Simulate clicking on the first tab (default behavior) + fireEvent.click(tabs[0]); + expect(tabs[0]).toHaveAttribute("aria-selected", "true"); + + // Clicking on other tabs (even if disabled, ensure no interaction) + fireEvent.click(tabs[1]); + expect(tabs[1]).not.toHaveAttribute("aria-selected", "true"); + }); + + it("should show content for the first tab by default", () => { + renderComponent(); + + const openingTabContent = screen.getByText(/Nothing to show yet/i); + expect(openingTabContent).toBeInTheDocument(); + }); + + it("should set the active tab based on URL query parameters", async () => { + const originalLocation = window.location; + delete window.location; + window.location = { + search: "?tab=openings", + ...originalLocation, + }; + + renderComponent(); + + const tabs = screen.getAllByRole("tab"); + await waitFor(() => { + expect(tabs[0]).toHaveAttribute("aria-selected", "true"); + }); + + window.location = originalLocation; // Reset location after the test + }); +}); \ No newline at end of file diff --git a/frontend/src/__test__/services/OpeningService.test.ts b/frontend/src/__test__/services/OpeningService.test.ts index 582d9152..47038c06 100644 --- a/frontend/src/__test__/services/OpeningService.test.ts +++ b/frontend/src/__test__/services/OpeningService.test.ts @@ -23,22 +23,22 @@ describe('OpeningService', () => { describe('fetchOpeningsPerYear', () => { it('should fetch openings per year successfully', async () => { const mockData = [ - { monthName: 'January', amount: 10 }, - { monthName: 'February', amount: 20 } + { monthName: 'Jan', amount: 10, statusCounts: { APP: 5, FG: 2 }, month: 1, year: 2023 }, + { monthName: 'Feb', amount: 20, statusCounts: { APP: 5, FG: 2 }, month: 2, year: 2023 } ]; (axios.get as vi.Mock).mockResolvedValue({ data: mockData }); - const props = { orgUnitCode: '001', statusCode: 'APP', entryDateStart: '2023-01-01', entryDateEnd: '2023-12-31' }; + const props = { orgUnitCode: ['001'], statusCode: ['APP'], entryDateStart: '2023-01-01', entryDateEnd: '2023-12-31' }; const result = await fetchOpeningsPerYear(props); - expect(axios.get).toHaveBeenCalledWith(`${backendUrl}/api/dashboard-metrics/submission-trends?orgUnitCode=001&statusCode=APP&entryDateStart=2023-01-01&entryDateEnd=2023-12-31`, { + expect(axios.get).toHaveBeenCalledWith(`${backendUrl}/api/users/submission-trends?orgUnitCode=001&statusCode=APP&entryDateStart=2023-01-01&entryDateEnd=2023-12-31`, { headers: { Authorization: `Bearer ${authToken}`, "Access-Control-Allow-Origin": "http://localhost:3000", "Content-Type": "application/json" } }); expect(result).toEqual([ - { group: 'Openings', key: 'January', value: 10 }, - { group: 'Openings', key: 'February', value: 20 } + { group: 'Openings', key: 'Jan 2023', value: 10, statusCount: { APP: 5, FG: 2 }, month: 1, year: 2023 }, + { group: 'Openings', key: 'Feb 2023', value: 20, statusCount: { APP: 5, FG: 2 }, month: 2, year: 2023 } ]); }); diff --git a/frontend/src/components/ActionButtons/index.tsx b/frontend/src/components/ActionButtons/index.tsx index 4aac3f17..b9bf5768 100644 --- a/frontend/src/components/ActionButtons/index.tsx +++ b/frontend/src/components/ActionButtons/index.tsx @@ -60,7 +60,7 @@ const ActionButtons: React.FC = ({ favorited, rowId }) => { className="align-self-stretch" hasIconOnly iconDescription="Document Download" - tooltipPosition="bottom-left" + tooltipPosition="right" kind="ghost" renderIcon={Icons.Download} onClick={() => null} diff --git a/frontend/src/components/BarChartGrouped/BarChartGrouped.scss b/frontend/src/components/BarChartGrouped/BarChartGrouped.scss index 002e72d8..79dd5bd3 100644 --- a/frontend/src/components/BarChartGrouped/BarChartGrouped.scss +++ b/frontend/src/components/BarChartGrouped/BarChartGrouped.scss @@ -11,5 +11,6 @@ min-height: 66px; } .bar-chart-container { - padding-bottom: 16px; + padding-top: 1rem; + padding-bottom: 1rem; } \ No newline at end of file diff --git a/frontend/src/components/BarChartGrouped/index.tsx b/frontend/src/components/BarChartGrouped/index.tsx index 2acd8ee9..2eb6aa0f 100644 --- a/frontend/src/components/BarChartGrouped/index.tsx +++ b/frontend/src/components/BarChartGrouped/index.tsx @@ -1,66 +1,79 @@ -import React, { useState, useEffect } from "react"; +// React imports +import React, { useState, useEffect, useRef } from "react"; +import ReactDOMServer from "react-dom/server"; + +// Third-party library imports import { GroupedBarChart, ScaleTypes } from "@carbon/charts-react"; -import { Dropdown, DatePicker, DatePickerInput } from "@carbon/react"; -import { fetchOpeningsPerYear } from "../../services/OpeningService"; -import { OpeningPerYearChart } from "../../types/OpeningPerYearChart"; +import { + FilterableMultiSelect, + DatePicker, + DatePickerInput, + Grid, + Column +} from "@carbon/react"; +import { differenceInDays, addDays, startOfMonth, endOfMonth, format } from "date-fns"; +import { useNavigate } from "react-router-dom"; + +// Styles import "@carbon/charts/styles.css"; import "./BarChartGrouped.scss"; -interface IDropdownItem { - value: string; - text: string; +// Utility functions +import { fetchOpeningsPerYear } from "../../services/OpeningService"; +import { fetchOrgUnits, status } from "../../services/search/openings"; +import { TextValueData, sortItems } from "../../utils/multiSelectSortUtils"; + +// Types +import { OpeningPerYearChart } from "../../types/OpeningPerYearChart"; + +// Local components +import BarChartTooltip from "../BarChartTooltip"; +import EmptySection from "../EmptySection"; + +interface MultiSelectEvent { + selectedItems: TextValueData[]; } -/** - * Renders an Bar Chart Grouped component. - * - * @returns {JSX.Element} The rendered BarChartGrouped component. - */ -function BarChartGrouped(): JSX.Element { +interface BarChartGroupedEvent { + detail: { + datum: OpeningPerYearChart; + } +} + +const BarChartGrouped = (): JSX.Element => { const [windowWidth, setWindowWidth] = useState(window.innerWidth); const [chartData, setChartData] = useState([]); const [isLoading, setIsLoading] = useState(true); - const [orgUnitCode, setOrgUnitCode] = useState(null); - const [statusCode, setStatusCode] = useState(null); const [startDate, setStartDate] = useState(null); const [endDate, setEndDate] = useState(null); + const [dateRange, setDateRange] = useState([]); + const [orgUnitItems, setOrgUnitItems] = useState([]); + const [statusItems, setStatusItems] = useState([]); + const [selectedOrgUnits, setSelectedOrgUnits] = useState([]); + const [selectedStatusCodes, setSelectedStatusCodes] = useState([]); + const [searchParameters, setSearchParameters] = useState(""); + const chartRef = useRef(null); + const navigate = useNavigate(); + const handleResize = () => { setWindowWidth(window.innerWidth); }; useEffect(() => { - const fetchChartData = async () => { - try { - setIsLoading(true); - let formattedStartDate: string | null = null; - let formattedEndDate: string | null = null; - if (startDate) { - formattedStartDate = formatDateToString(startDate); - } - if (endDate) { - formattedEndDate = formatDateToString(endDate); - } - - const data: OpeningPerYearChart[] = await fetchOpeningsPerYear({ - orgUnitCode, - statusCode, - entryDateStart: formattedStartDate, - entryDateEnd: formattedEndDate, - }); - setChartData(data); - setIsLoading(false); + const fetchOrgUnitsData = async () => { + try { + const data = await fetchOrgUnits(); + setOrgUnitItems(data.map((orgUnit) => ({ value: orgUnit.orgUnitCode, text: orgUnit.orgUnitName }))); } catch (error) { - console.error("Error fetching chart data:", error); - setIsLoading(false); + console.error("Error fetching org units:", error); } }; + setStatusItems(status); + fetchOrgUnitsData(); - fetchChartData(); - window.addEventListener("resize", handleResize); - return () => window.removeEventListener("resize", handleResize); - }, [orgUnitCode, statusCode, startDate, endDate]); + },[]); const formatDateToString = (dateToFormat: Date) => { if (!dateToFormat) return null; @@ -70,136 +83,221 @@ function BarChartGrouped(): JSX.Element { return `${year}-${month}-${day}`; }; - const colors = { - Openings: "#1192E8", - }; + const tooltip = (data:OpeningPerYearChart[], defaultHTML: string, datum: OpeningPerYearChart) => { + const tooltipContent = ; + return ReactDOMServer.renderToString(tooltipContent); + } + + const colors = { Openings: "#1192E8" }; const options = { axes: { - left: { - mapsTo: "value", - }, + left: { mapsTo: "value" }, bottom: { scaleType: ScaleTypes.LABELS, - mapsTo: "key", - }, + mapsTo: "key" + } }, color: { - scale: colors, + scale: colors }, height: "18.5rem", grid: { x: { enabled: false, color: "#d3d3d3", - strokeDashArray: "2,2", + strokeDashArray: "2,2" }, y: { enabled: true, color: "#d3d3d3", - strokeDashArray: "2,2", - }, + strokeDashArray: "2,2" + } }, toolbar: { enabled: false, numberOfIcons: 2, controls: [ { - type: "Make fullscreen", + type: "Make fullscreen" }, { - type: "Make fullscreen", - }, - ], + type: "Make fullscreen" + } + ] }, + tooltip:{ + enabled: true, + customHTML: tooltip + } }; - const orgUnitItems = [ - { value: "DCR", text: "DCR" }, - { value: "XYZ", text: "District 2" }, - // Add more options as needed - ]; - - const statusItems = [ - { value: "APP", text: "Approved" }, - { value: "NAN", text: "Not Approved" }, - // Add more options as needed - ]; - - const setOrgUnitCodeSelected = ({ - selectedItem, - }: { - selectedItem: IDropdownItem; - }) => { - setOrgUnitCode(selectedItem.value); - }; + const setDates = (dates: Date[]) => { + setDateRange(dates); + // Only apply dates if we have both selected + if(dates.length === 2) { + // If the difference between the dates is greater than 365 days, set the end date to 365 days after the start date + if(differenceInDays(dates[1], dates[0]) > 365){ + dates[1] = addDays(dates[0], 365); + } + // Set the start and end date + setStartDate(dates[0]); + setEndDate(dates[1]); + } else { + setStartDate(null); + setEndDate(null); + } + } - const setStatusCodeSelected = ({ - selectedItem, - }: { - selectedItem: IDropdownItem; - }) => { - setStatusCode(selectedItem.value); - }; + const maxDate = () => formatDateToString(new Date()); + + useEffect(() =>{ + + const fetchChartData = async () => { + try { + + const searchValues: string[] = []; + + setIsLoading(true); + let formattedStartDate: string | null = null; + let formattedEndDate: string | null = null; + + if (startDate) { + formattedStartDate = formatDateToString(startDate); + searchValues.push(`Start Date: ${formattedStartDate}`); + } + if (endDate) { + formattedEndDate = formatDateToString(endDate); + searchValues.push(`End Date: ${formattedEndDate}`); + } + + const orgUnits = selectedOrgUnits?.map((orgUnit) => orgUnit.value); + const statusCodes = selectedStatusCodes?.map((statusCode) => statusCode.value); + + + if(orgUnits.length > 0) { + searchValues.push(`Districts: ${orgUnits.join(", ")}`); + } + if(statusCodes.length > 0) { + searchValues.push(`Status: ${statusCodes.join(", ")}`); + } + + setSearchParameters(searchValues.join(", ")); + + const data: OpeningPerYearChart[] = await fetchOpeningsPerYear({ + orgUnitCode: orgUnits, + statusCode: statusCodes, + entryDateStart: formattedStartDate, + entryDateEnd: formattedEndDate + }); + setChartData(data); + setIsLoading(false); + } catch (error) { + console.error("Error fetching chart data:", error); + setIsLoading(false); + } + }; + fetchChartData(); + window.addEventListener("resize", handleResize); + return () => window.removeEventListener("resize", handleResize); + + },[selectedOrgUnits, selectedStatusCodes, startDate, endDate]); + + useEffect(() => { + if(chartRef.current){ + const { chart }:GroupedBarChart = chartRef.current; + if(chart){ + chart.services.events.addEventListener("bar-click", (event: BarChartGroupedEvent) => { + const { datum } = event.detail; + const searchDateStart = format(startOfMonth(new Date(datum.year, datum.month - 1)), "yyyy-MM-dd"); + const searchDateEnd = format(endOfMonth(new Date(datum.year, datum.month - 1)), "yyyy-MM-dd"); + navigate(`/silviculture-search?tab=openings&dateType=Update&startDate=${searchDateStart}&endDate=${searchDateEnd}`); + }); + } + } + },[chartRef,isLoading]); return ( -
-
-
- (item ? item.text : "")} - onChange={setOrgUnitCodeSelected} - label="District" + + +

{searchParameters}

+ (item ? `${item.value} - ${item.text}` : "")} + selectionFeedback="top-after-reopen" + onChange={(e: MultiSelectEvent) => setSelectedOrgUnits(e.selectedItems)} + selectedItems={selectedOrgUnits} + sortItems={sortItems} + placeholder="Filter by district" + /> +
+ + + (item ? `${item.value} - ${item.text}` : "")} + selectionFeedback="top-after-reopen" + onChange={(e: MultiSelectEvent) => setSelectedStatusCodes(e.selectedItems)} + selectedItems={selectedStatusCodes} + sortItems={sortItems} + placeholder="Filter by status" + /> + + + + + -
-
- (item ? item.text : "")} - onChange={setStatusCodeSelected} - label="Status" + -
-
- setStartDate(dates[0])} - > - - -
-
- setEndDate(dates[0])} - > - - -
-
- {isLoading ? ( -

Loading...

- ) : ( -
- -
- )} -
+ + + + + {isLoading ? ( +

Loading...

+ ) : ( +
+ {chartData.length === 0 && !searchParameters && } + {chartData.length === 0 && searchParameters && } + {chartData.length > 0 && } +
+ )} +
+ ); }; diff --git a/frontend/src/components/BarChartTooltip/index.scss b/frontend/src/components/BarChartTooltip/index.scss new file mode 100644 index 00000000..ddf024ae --- /dev/null +++ b/frontend/src/components/BarChartTooltip/index.scss @@ -0,0 +1,18 @@ +@use '@bcgov-nr/nr-theme/design-tokens/colors.scss' as colors; +@use '@bcgov-nr/nr-theme/design-tokens/variables.scss' as vars; +@use '@carbon/type'; + +ul.bar--tooltip_status { + margin-bottom: 1rem; +} + +ul.bar--tooltip_status li { + list-style-type: disc; + margin-left: 2.5rem; + @include type.type-style('body-compact-02'); + font-weight: 400; + font-size: 14px; + line-height: 18px; + letter-spacing: 0.16px; + color: var(--bx-text-secondary); +} \ No newline at end of file diff --git a/frontend/src/components/BarChartTooltip/index.tsx b/frontend/src/components/BarChartTooltip/index.tsx new file mode 100644 index 00000000..5d331d0b --- /dev/null +++ b/frontend/src/components/BarChartTooltip/index.tsx @@ -0,0 +1,27 @@ +import React from "react"; +import ChartTitle from "../ChartTitle"; +import { OpeningPerYearChart } from "../../types/OpeningPerYearChart"; +import { status } from "../../services/search/openings"; +import './index.scss' + +interface BarChartTooltipProps { + datum: OpeningPerYearChart +} + +const BarChartTooltip: React.FC = ({ datum }) => { + + const statusDescription = (statusCode: string) => ( + status.find((statusData) => statusData.value === statusCode)?.text ?? statusCode + ).toLowerCase(); + + return ( +
+ +
    + {Object.keys(datum.statusCount).map((key) => (
  • {datum.statusCount[key]} {statusDescription(key)}

  • ))} +
+
+ ); +}; + +export default BarChartTooltip; \ No newline at end of file diff --git a/frontend/src/components/SilvicultureSearch/Openings/OpeningsSearchTab/index.tsx b/frontend/src/components/SilvicultureSearch/Openings/OpeningsSearchTab/index.tsx index 9058805a..3e7c4641 100644 --- a/frontend/src/components/SilvicultureSearch/Openings/OpeningsSearchTab/index.tsx +++ b/frontend/src/components/SilvicultureSearch/Openings/OpeningsSearchTab/index.tsx @@ -1,16 +1,25 @@ import React, { useState, useEffect, useContext } from "react"; + +// Styles import "./styles.scss"; + +// Utility functions +import { searchScreenColumns } from "../../../../constants/tableConstants"; +import { useOpeningsQuery } from "../../../../services/queries/search/openingQueries"; +import { useOpeningsSearch } from "../../../../contexts/search/OpeningsSearch"; +import { countActiveFilters } from "../../../../utils/searchUtils"; + +// Types +import { ITableHeader } from "../../../../types/TableHeader"; +import { OpeningFilters } from "../../../../services/search/openings"; + +// Local components import EmptySection from "../../../EmptySection"; import OpeningsSearchBar from "../OpeningsSearchBar"; import TableSkeleton from "../../../TableSkeleton"; import SearchScreenDataTable from "../SearchScreenDataTable"; -import { searchScreenColumns } from "../../../../constants/tableConstants"; import OpeningsMap from "../../../OpeningsMap"; -import { useOpeningsQuery } from "../../../../services/queries/search/openingQueries"; -import { useOpeningsSearch } from "../../../../contexts/search/OpeningsSearch"; import PaginationContext from "../../../../contexts/PaginationContext"; -import { ITableHeader } from "../../../../types/TableHeader"; -import { countActiveFilters } from "../../../../utils/searchUtils"; const OpeningsSearchTab: React.FC = () => { const [showSpatial, setShowSpatial] = useState(false); @@ -22,12 +31,13 @@ const OpeningsSearchTab: React.FC = () => { const [isNoFilterSearch, setIsNoFilterSearch] = useState(false); // Handles the notification for no filters applied const { currentPage, itemsPerPage } = useContext(PaginationContext); const [selectedOpeningIds,setSelectedOpeningIds] = useState([]); + const [hasExternalParams, setHasExternalParams] = useState(false); const [headers, setHeaders] = useState(searchScreenColumns); // Only fetch when search is triggered and with finalParams const { data, isFetching } = useOpeningsQuery(finalParams, isSearchTriggered); - const { filters, searchTerm } = useOpeningsSearch(); + const { filters, searchTerm, setFilters } = useOpeningsSearch(); const toggleSpatial = () => { setShowSpatial(!showSpatial); @@ -114,12 +124,38 @@ const OpeningsSearchTab: React.FC = () => { handleSearchInputChange(searchTerm); },[searchTerm]); - //initally when the screen loads check if there was a earch term present - useEffect (()=>{ + useEffect(() => { + if(hasExternalParams){ + handleSearch(); + } + },[hasExternalParams]); + + // Check if we have query parms and if the params align with the filter fields + useEffect(() => { + // Get the query params + const urlParams = new URLSearchParams(window.location.search); + let hasParams = false; + + // Here we do a match between the query params and the filter fields + Object.keys(filters).forEach((key) => { + // This is to avoid setting the filter fields with the query params if they don't exist on the filter + if(urlParams.has(key)){ + hasParams = true; + setFilters((prevFilters: OpeningFilters) => ({ + ...prevFilters, + [key]: urlParams.get(key) + })); + } + }); + + //initally when the screen loads check if there was a earch term present if(searchTerm.length>0 || countActiveFilters(filters)>0){ handleSearch(); } - },[]) + + setHasExternalParams(hasParams); + + },[]); return ( <> diff --git a/frontend/src/screens/SilvicultureSearch/index.tsx b/frontend/src/screens/SilvicultureSearch/index.tsx index c50dae59..a6394f29 100644 --- a/frontend/src/screens/SilvicultureSearch/index.tsx +++ b/frontend/src/screens/SilvicultureSearch/index.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; import PageTitle from "../../components/PageTitle"; import './SilvicultureSearch.scss' import { @@ -12,6 +12,19 @@ import * as Icons from '@carbon/icons-react'; import OpeningsSearchTab from "../../components/SilvicultureSearch/Openings/OpeningsSearchTab"; const SilvicultureSearch: React.FC = () => { + const [activeTab, setActiveTab] = useState(0); // Track active tab index + + const tabChange = (tabSelection:{selectedIndex: number}) => { + setActiveTab(tabSelection.selectedIndex); + }; + + useEffect(() => { + const urlParams = new URLSearchParams(window.location.search); + + if(urlParams.has('tab') && urlParams.get('tab')?.includes('openings')) { + setActiveTab(0); + } + },[]); return ( <> @@ -24,7 +37,7 @@ const SilvicultureSearch: React.FC = () => { - +
Openings
Activities
diff --git a/frontend/src/services/OpeningService.ts b/frontend/src/services/OpeningService.ts index 257a40ce..fd1eb0b6 100644 --- a/frontend/src/services/OpeningService.ts +++ b/frontend/src/services/OpeningService.ts @@ -8,6 +8,7 @@ import { IFreeGrowingProps, IFreeGrowingChartData } from '../types/OpeningTypes'; +import { API_ENDPOINTS, defaultHeaders } from './apiConfig'; const backendUrl = env.VITE_BACKEND_URL; @@ -19,33 +20,42 @@ const backendUrl = env.VITE_BACKEND_URL; export async function fetchOpeningsPerYear(props: IOpeningPerYear): Promise { const authToken = getAuthIdToken(); try { - // Construct URL with optional parameters - let url = backendUrl.concat("/api/dashboard-metrics/submission-trends"); - if (props.orgUnitCode || props.statusCode || props.entryDateStart || props.entryDateEnd) { - url += '?'; - if (props.orgUnitCode) url += `orgUnitCode=${props.orgUnitCode}&`; - if (props.statusCode) url += `statusCode=${props.statusCode}&`; - if (props.entryDateStart) url += `entryDateStart=${props.entryDateStart}&`; - if (props.entryDateEnd) url += `entryDateEnd=${props.entryDateEnd}&`; - // Remove trailing '&' if present - url = url.replace(/&$/, ''); + const args: string[] = []; + + if(props.orgUnitCode) { + props.orgUnitCode.forEach((orgUnit) => { + args.push(`orgUnitCode=${orgUnit}`); + }); } - const response = await axios.get(url, { - headers: { - 'Content-Type': 'application/json', - 'Access-Control-Allow-Origin': window.location.origin, - Authorization: `Bearer ${authToken}` - } - }); + if(props.statusCode) { + props.statusCode.forEach((status) => { + args.push(`statusCode=${status}`); + }); + } + + if(props.entryDateStart) { + args.push(`entryDateStart=${props.entryDateStart}`); + } + + if(props.entryDateEnd) { + args.push(`entryDateEnd=${props.entryDateEnd}`); + } + + const urlParams = args.join('&'); + + const response = await axios.get(API_ENDPOINTS.submissionTrends(urlParams), defaultHeaders(authToken)); const { data } = response; if (data && Array.isArray(data)) { // Format data for BarChartGrouped component const formattedData: OpeningPerYearChart[] = data.map(item => ({ group: "Openings", - key: item.monthName, - value: item.amount + key: `${item.monthName} ${item.year}`, + year: item.year, + month: item.month, + value: item.amount, + statusCount: item.statusCounts })); return formattedData; diff --git a/frontend/src/services/apiConfig.ts b/frontend/src/services/apiConfig.ts index ec9c22b5..e919407f 100644 --- a/frontend/src/services/apiConfig.ts +++ b/frontend/src/services/apiConfig.ts @@ -1,6 +1,10 @@ // Centralized API configuration file import { env } from '../env'; +const resolveQueryString = (queryString: string | null) => { + return queryString ? `?${queryString}` : ''; +} + // Define the API base URL from the environment variables const API_BASE_URL = env.VITE_BACKEND_URL; @@ -13,7 +17,8 @@ const API_ENDPOINTS = { categories: () => `${API_BASE_URL}/api/opening-search/categories`, orgUnits: () => `${API_BASE_URL}/api/opening-search/org-units`, clientsByNameAcronymNumber: (query: string) => `${API_BASE_URL}/api/forest-clients/byNameAcronymNumber?value=${query}`, - clientLocations: (clientId: string) => `${API_BASE_URL}/api/forest-clients/${clientId}/locations` + clientLocations: (clientId: string) => `${API_BASE_URL}/api/forest-clients/${clientId}/locations`, + submissionTrends: (queryString: string | null) => `${API_BASE_URL}/api/users/submission-trends${resolveQueryString(queryString)}` }; // Define the default headers for the API requests, including ones used by CORS diff --git a/frontend/src/services/search/openings.ts b/frontend/src/services/search/openings.ts index acdb83eb..1c04478f 100644 --- a/frontend/src/services/search/openings.ts +++ b/frontend/src/services/search/openings.ts @@ -4,6 +4,7 @@ import { getAuthIdToken } from "../AuthService"; import { dateTypes, blockStatuses } from "../../mock-data/openingSearchFilters"; import { createDateParams } from "../../utils/searchUtils"; import { API_ENDPOINTS, defaultHeaders } from "../apiConfig"; +import { TextValueData } from "../../utils/multiSelectSortUtils"; export interface OpeningFilters { searchInput?: string; @@ -55,6 +56,28 @@ export interface OpeningItem { silvaReliefAppId: string | null; } +export interface OrgUnit { + orgUnitNo: number; + orgUnitCode: string; + orgUnitName: string; +} + +export interface CodeDescription { + code: string; + description: string; +} + +export const status: TextValueData[] = [ + {value:'AMG', text: 'Amalgamate'}, + {value:'AMD', text: 'Amended'}, + {value:'APP', text: 'Approved'}, + {value:'DFT', text: 'Draft'}, + {value:'FG', text: 'Free Growing'}, + {value:'RMD', text: 'Removed'}, + {value:'RET', text: 'Retired'}, + {value:'SUB', text: 'Submitted'} +]; + export const fetchOpenings = async (filters: OpeningFilters): Promise => { // Get the date params based on dateType // Get the date params based on dateType @@ -139,7 +162,7 @@ export const fetchUserRecentOpenings = async (limit: number): Promise => { }; }; -export const fetchCategories = async (): Promise => { +export const fetchCategories = async (): Promise => { // Retrieve the auth token const authToken = getAuthIdToken(); @@ -150,7 +173,7 @@ export const fetchCategories = async (): Promise => { return response.data; }; -export const fetchOrgUnits = async (): Promise => { +export const fetchOrgUnits = async (): Promise => { // Retrieve the auth token const authToken = getAuthIdToken(); diff --git a/frontend/src/types/OpeningPerYearChart.ts b/frontend/src/types/OpeningPerYearChart.ts index 9f2a009a..a40ef642 100644 --- a/frontend/src/types/OpeningPerYearChart.ts +++ b/frontend/src/types/OpeningPerYearChart.ts @@ -2,4 +2,7 @@ export interface OpeningPerYearChart { group: string; key: string; value: string; + year: number; + month: number; + statusCount: Record; } diff --git a/frontend/src/types/OpeningTypes.ts b/frontend/src/types/OpeningTypes.ts index 34969a73..c337422c 100644 --- a/frontend/src/types/OpeningTypes.ts +++ b/frontend/src/types/OpeningTypes.ts @@ -18,8 +18,8 @@ export interface RecentOpeningApi { } export interface IOpeningPerYear { - orgUnitCode: string | null; - statusCode: string | null; + orgUnitCode: string[] | null; + statusCode: string[] | null; entryDateStart: string | null; entryDateEnd: string | null; } diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 7bc6583f..b64bca9a 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "types": ["vite/client"], + "types": ["vite/client","vitest/globals","@testing-library/jest-dom"], "target": "ESNext", "lib": [ "dom", diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index dd88981c..c043798f 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,11 +1,15 @@ +/// +import { defineConfig } from 'vite' import { fileURLToPath, URL } from 'node:url'; -import { defineConfig, UserConfig } from 'vite' import react from '@vitejs/plugin-react' // https://vitejs.dev/config/ export default defineConfig(({ mode }) => { - const config: UserConfig = { - define: {}, + const define = { + global:{} + } + return { + define, plugins: [ { name: 'build-html', @@ -45,13 +49,27 @@ export default defineConfig(({ mode }) => { '@': fileURLToPath(new URL('./src', import.meta.url)) } }, - }; - - if (mode === 'development') { - if (config.define) { - config.define.global = {}; + test: { + globals: true, + environment: 'jsdom', + setupFiles: './src/setupTests.ts', + css: false, + coverage: { + provider: 'v8', + reporter: ['lcov', 'cobertura', 'html'], + include: ['src/**/*'], + exclude: [ + 'src/amplifyconfiguration.ts', + 'src/module.d.ts', + 'src/react-app-env.d.ts', + 'src/reportWebVitals.ts' + ], + }, + server: { + deps: { + inline: [/^(?!.*vitest).*$/] + } + } } - } - - return config; + }; }); diff --git a/frontend/vitest.config.ts b/frontend/vitest.config.ts deleted file mode 100644 index a1c885b2..00000000 --- a/frontend/vitest.config.ts +++ /dev/null @@ -1,29 +0,0 @@ -/// -import { defineConfig } from 'vitest/config'; -import react from '@vitejs/plugin-react'; - -export default defineConfig({ - plugins: [react()], - test: { - globals: true, - environment: 'jsdom', - setupFiles: './src/setupTests.ts', - css: false, - coverage: { - provider: 'v8', - reporter: ['lcov', 'cobertura', 'html'], - include: ['src/**/*'], - exclude: [ - 'src/amplifyconfiguration.ts', - 'src/module.d.ts', - 'src/react-app-env.d.ts', - 'src/reportWebVitals.ts' - ], - }, - server: { - deps: { - inline: [/^(?!.*vitest).*$/] - } - }, - }, -});