diff --git a/.github/workflows/.deploy.yml b/.github/workflows/.deploy.yml new file mode 100644 index 00000000..a5b5f2b1 --- /dev/null +++ b/.github/workflows/.deploy.yml @@ -0,0 +1,195 @@ +name: .Deploys + +on: + workflow_call: + inputs: + ### Required + # Nothing! Only `secrets: inherit` is required + + ### Typical / recommended + environment: + description: GitHub/OpenShift environment; usually PR number, test or prod + default: '' + required: false + type: string + tag: + description: Container tag; usually PR number + default: ${{ github.event.number }} + required: false + type: string + target: + description: Deployment target; usually PR number, test or prod + default: ${{ github.event.number }} + required: false + type: string + + outputs: + run_tests: + description: Run Cypress tests if the core apps have changed (excludes sync) + value: ${{ jobs.init.outputs.deploy_core }} + +jobs: + init: + name: Deploy (init) + environment: ${{ inputs.environment }} + outputs: + fam-modded-zone: ${{ steps.fam-modded-zone.outputs.fam-modded-zone }} + deploy_core: ${{ steps.triggers.outputs.core }} + deploy_sync: ${{ steps.triggers.outputs.sync }} + runs-on: ubuntu-latest + steps: + # Check triggers (omitted or matched) + - name: Check core triggers + uses: bcgov-nr/action-diff-triggers@v0.2.0 + id: check_core + with: + triggers: ('backend/' 'common/' 'database/' 'frontend/' 'oracle-api/') + + - name: Check sync triggers + uses: bcgov-nr/action-diff-triggers@v0.2.0 + id: check_sync + with: + triggers: ('common/' 'sync/') + + # Simplify triggers + - name: Simplify triggers + id: triggers + run: | + echo "core=${{ github.event_name != 'pull_request' || steps.check_core.outputs.triggered == 'true' }}" >> $GITHUB_OUTPUT + echo "sync=${{ github.event_name != 'pull_request' || steps.check_sync.outputs.triggered == 'true' }}" >> $GITHUB_OUTPUT + + - name: FAM routing + id: fam-modded-zone + if: steps.triggers.outputs.core == 'true' + run: | + if [ ${{ github.event_name }} == 'pull_request' ]; then + echo "fam-modded-zone=$(( ${{ inputs.target }} % 50 ))" >> $GITHUB_OUTPUT + else + echo "fam-modded-zone=${{ inputs.target }}" >> $GITHUB_OUTPUT + fi + + - name: OpenShift Init + if: steps.triggers.outputs.core == 'true' || steps.triggers.outputs.sync == 'true' + uses: bcgov-nr/action-deployer-openshift@v3.0.1 + with: + oc_namespace: ${{ vars.OC_NAMESPACE }} + oc_server: ${{ vars.OC_SERVER }} + oc_token: ${{ secrets.OC_TOKEN }} + file: common/openshift.init.yml + overwrite: true + parameters: + -p ZONE=${{ inputs.target }} + -p DB_PASSWORD='${{ secrets.DB_PASSWORD }}' + -p FORESTCLIENTAPI_KEY='${{ secrets.FORESTCLIENTAPI_KEY }}' + -p ORACLE_PASSWORD='${{ secrets.ORACLE_PASSWORD }}' + -p ORACLE_SERVICE='${{ vars.ORACLE_SERVICE }}' + -p ORACLE_USER='${{ vars.ORACLE_USER }}' + -p ORACLE_SYNC_USER='${{ vars.ORACLE_SYNC_USER }}' + -p ORACLE_SYNC_PASSWORD='${{ secrets.ORACLE_SYNC_PASSWORD }}' + -p ORACLE_CERT_SECRET='${{ secrets.ORACLE_CERT_SECRET }}' + -p ORACLE_HOST='${{ vars.ORACLE_HOST }}' + -p VITE_USER_POOLS_WEB_CLIENT_ID=${{ secrets.VITE_USER_POOLS_WEB_CLIENT_ID }} + + - name: Database + if: steps.triggers.outputs.core == 'true' || steps.triggers.outputs.sync == 'true' + uses: bcgov-nr/action-deployer-openshift@v3.0.1 + with: + oc_namespace: ${{ vars.OC_NAMESPACE }} + oc_server: ${{ vars.OC_SERVER }} + oc_token: ${{ secrets.OC_TOKEN }} + file: common/openshift.database.yml + overwrite: false + parameters: + -p ZONE=${{ inputs.target }} + ${{ github.event_name == 'pull_request' && '-p DB_PVC_SIZE=192Mi' || '' }} + ${{ github.event_name == 'pull_request' && '-p MEMORY_REQUEST=100Mi' || '' }} + ${{ github.event_name == 'pull_request' && '-p MEMORY_LIMIT=200Mi' || '' }} + + deploy: + name: Deploy + environment: ${{ inputs.environment }} + if: needs.init.outputs.deploy_core == 'true' + needs: [init] + runs-on: ubuntu-latest + timeout-minutes: 10 + strategy: + matrix: + name: [backend, frontend, oracle-api] + include: + - name: backend + file: backend/openshift.deploy.yml + overwrite: true + parameters: + -p AWS_COGNITO_ISSUER_URI=https://cognito-idp.ca-central-1.amazonaws.com/${{ vars.VITE_USER_POOLS_ID }} + verification_path: "health" + - name: frontend + file: frontend/openshift.deploy.yml + overwrite: true + parameters: + -p FAM_MODDED_ZONE=${{ needs.init.outputs.fam-modded-zone }} + -p VITE_SPAR_BUILD_VERSION=snapshot-${{ inputs.target || github.event.number }} + -p VITE_USER_POOLS_ID=${{ vars.VITE_USER_POOLS_ID }} + - name: oracle-api + file: oracle-api/openshift.deploy.yml + overwrite: true + parameters: + -p AWS_COGNITO_ISSUER_URI=https://cognito-idp.ca-central-1.amazonaws.com/${{ vars.VITE_USER_POOLS_ID }} + ${{ github.event_name == 'pull_request' && '-p CPU_LIMIT=100m' || '' }} + ${{ inputs.target == 'prod' && '-p MIN_REPLICAS=3' || '' }} + ${{ inputs.target == 'prod' && '-p MAX_REPLICAS=5' || '' }} + verification_path: "actuator/health" + + steps: + - uses: bcgov-nr/action-deployer-openshift@v3.0.1 + id: deploys + with: + file: ${{ matrix.file }} + oc_namespace: ${{ vars.OC_NAMESPACE }} + oc_server: ${{ vars.OC_SERVER }} + oc_token: ${{ secrets.OC_TOKEN }} + overwrite: ${{ matrix.overwrite }} + parameters: + -p TAG=${{ inputs.tag }} + -p ZONE=${{ inputs.target }} + ${{ github.event_name == 'pull_request' && '-p MIN_REPLICAS=1' || '' }} + ${{ github.event_name == 'pull_request' && '-p MAX_REPLICAS=1' || '' }} + ${{ matrix.parameters }} + verification_path: ${{ matrix.verification_path }} + verification_retry_attempts: 5 + verification_retry_seconds: 20 + + # ETL testing will only run on Pull Requests if the sync/ directory is modified + sync: + name: Deploy (sync) + environment: ${{ inputs.environment }} + if: needs.init.outputs.deploy_sync == 'true' + needs: [init] + runs-on: ubuntu-latest + steps: + - name: Deploy (sync) + uses: bcgov-nr/action-deployer-openshift@v3.0.1 + with: + file: sync/openshift.deploy.yml + oc_namespace: ${{ vars.OC_NAMESPACE }} + oc_server: ${{ vars.OC_SERVER }} + oc_token: ${{ secrets.OC_TOKEN }} + overwrite: true + parameters: + -p TAG=${{ inputs.tag }} + -p ZONE=${{ inputs.target }} + ${{ github.event_name == 'pull_request' && '-p TEST_MODE=true' || '' }} + + + - name: Override OpenShift version + if: github.event_name == 'pull_request' + env: + OC: https://mirror.openshift.com/pub/openshift-v4/clients/ocp/stable-4.13/openshift-client-linux.tar.gz + run: | + # Download and extract with retry, continuing on error + (wget ${{ env.OC }} -qcO - | tar -xzvf - oc)|| !! || true + oc version + working-directory: /usr/local/bin/ + + - name: Run sync ETL + if: github.event_name == 'pull_request' + run: ./sync/oc_run.sh ${{ inputs.tag }} ${{ secrets.oc_token }} diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml index 0a79ef2c..92f6d86b 100644 --- a/.github/workflows/analysis.yml +++ b/.github/workflows/analysis.yml @@ -15,7 +15,7 @@ jobs: tests-backend: name: Tests (Backend) if: github.event_name != 'pull_request' || !github.event.pull_request.draft - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest steps: - uses: bcgov-nr/action-test-and-analyse-java@v1.0.2 with: @@ -37,7 +37,7 @@ jobs: lint-frontend: name: Lint (Frontend) if: github.event_name != 'pull_request' || !github.event.pull_request.draft - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest steps: - uses: bcgov-nr/action-test-and-analyse@v1.2.1 with: @@ -52,7 +52,7 @@ jobs: tests-frontend: name: Tests (Frontend) if: github.event_name != 'pull_request' || !github.event.pull_request.draft - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest steps: - uses: bcgov-nr/action-test-and-analyse@v1.2.1 env: @@ -72,7 +72,7 @@ jobs: -Dsonar.javascript.lcov.reportPaths=coverage/lcov.info -Dsonar.typescript.tsconfigPaths=tsconfig.json -Dsonar.sources=src/ - -Dsonar.exclusions=src/__test__/** + -Dsonar.exclusions=src/__test__/**,src/amplifyconfiguration.*,src/**/*.scss,src/**/*.css,src/**/*.d.*,src/setupTests.* -Dsonar.tests=src/__test__/ -Dsonar.project.monorepo.enabled=true sonar_token: ${{ secrets.SONAR_TOKEN_FRONTEND }} @@ -83,7 +83,7 @@ jobs: trivy: name: Trivy Security Scan if: github.event_name != 'pull_request' || !github.event.pull_request.draft - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -107,6 +107,6 @@ jobs: name: Analysis Results if: always() && (!failure()) && (!cancelled()) needs: [lint-frontend, tests-backend, tests-frontend] # Include trivy when/if it gets back to being reliable - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest steps: - run: echo "Workflow completed successfully!" diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml index b0f39c68..c6942fb3 100644 --- a/.github/workflows/merge.yml +++ b/.github/workflows/merge.yml @@ -14,7 +14,7 @@ jobs: init-test: name: TEST Init environment: test - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest steps: - name: OpenShift Init uses: bcgov-nr/action-deployer-openshift@v3.0.1 @@ -41,7 +41,7 @@ jobs: name: TEST Deployments needs: [init-test] environment: test - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest permissions: issues: write strategy: @@ -87,7 +87,7 @@ jobs: name: PROD Init needs: [deploys-test] environment: prod - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest steps: - name: OpenShift Init uses: bcgov-nr/action-deployer-openshift@v3.0.1 @@ -113,7 +113,7 @@ jobs: image-promotions: name: Promote images to PROD needs: [deploys-test] - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest permissions: packages: write strategy: @@ -131,7 +131,7 @@ jobs: name: PROD Deployments needs: [init-prod, image-promotions] environment: prod - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest strategy: matrix: name: [database, backend, frontend, fluentbit] diff --git a/.github/workflows/pr-close.yml b/.github/workflows/pr-close.yml index d6695786..3ad1673f 100644 --- a/.github/workflows/pr-close.yml +++ b/.github/workflows/pr-close.yml @@ -15,7 +15,7 @@ jobs: cleanup-openshift: name: Cleanup OpenShift if: '!github.event.pull_request.head.repo.fork' - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest steps: - uses: redhat-actions/openshift-tools-installer@v1 with: @@ -32,7 +32,7 @@ jobs: image-promotions: name: Image Promotions if: github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'main' - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest strategy: matrix: package: [database, backend, frontend] diff --git a/.github/workflows/pr-open.yml b/.github/workflows/pr-open.yml index b7ccb11d..7cea2962 100644 --- a/.github/workflows/pr-open.yml +++ b/.github/workflows/pr-open.yml @@ -17,7 +17,7 @@ jobs: if: "!github.event.pull_request.head.repo.fork" outputs: route: ${{ github.event.number }} - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest permissions: pull-requests: write steps: @@ -70,7 +70,7 @@ jobs: builds: name: Builds - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest permissions: packages: write strategy: @@ -96,7 +96,7 @@ jobs: deploys: name: Deploys needs: [builds, init] - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest strategy: matrix: name: [database, backend, frontend, fluentbit] diff --git a/backend/pom.xml b/backend/pom.xml index 6d3f6fcd..6b4b0cbf 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -291,7 +291,7 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.5.1 + 3.5.2 integration-tests @@ -318,7 +318,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.1 + 3.5.2 @{argLine} -Xmx1024m ${skip.unit.tests} @@ -545,7 +545,7 @@ com.nimbusds nimbus-jose-jwt - 9.42 + 9.43 org.testcontainers diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/common/exception/UserFavoriteNotFoundException.java b/backend/src/main/java/ca/bc/gov/restapi/results/common/exception/UserFavoriteNotFoundException.java new file mode 100644 index 00000000..5d931aa3 --- /dev/null +++ b/backend/src/main/java/ca/bc/gov/restapi/results/common/exception/UserFavoriteNotFoundException.java @@ -0,0 +1,9 @@ +package ca.bc.gov.restapi.results.common.exception; + +public class UserFavoriteNotFoundException extends NotFoundGenericException { + + public UserFavoriteNotFoundException() { + super("UserFavoriteEntity"); + } + +} diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/SilvaOracleConstants.java b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/SilvaOracleConstants.java index 68f3732c..dc6abd60 100644 --- a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/SilvaOracleConstants.java +++ b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/SilvaOracleConstants.java @@ -9,6 +9,7 @@ public class SilvaOracleConstants { public static final String ORG_UNIT = "orgUnit"; public static final String CATEGORY = "category"; public static final String STATUS_LIST = "statusList"; + public static final String OPENING_IDS = "openingIds"; public static final String MY_OPENINGS = "myOpenings"; public static final String SUBMITTED_TO_FRPA = "submittedToFrpa"; public static final String DISTURBANCE_DATE_START = "disturbanceDateStart"; diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/dto/OpeningSearchFiltersDto.java b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/dto/OpeningSearchFiltersDto.java index 62fc0296..bb8d64ab 100644 --- a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/dto/OpeningSearchFiltersDto.java +++ b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/dto/OpeningSearchFiltersDto.java @@ -14,8 +14,8 @@ @Getter @ToString public class OpeningSearchFiltersDto { - private final String orgUnit; - private final String category; + private final List orgUnit; + private final List category; private final List statusList; private final Boolean myOpenings; private final Boolean submittedToFrpa; @@ -35,11 +35,12 @@ public class OpeningSearchFiltersDto { @Setter private String requestUserId; + private List openingIds; /** Creates an instance of the search opening filter dto. */ public OpeningSearchFiltersDto( - String orgUnit, - String category, + List orgUnit, + List category, List statusList, Boolean myOpenings, Boolean submittedToFrpa, @@ -55,9 +56,20 @@ public OpeningSearchFiltersDto( String cutBlockId, String timberMark, String mainSearchTerm) { - this.orgUnit = Objects.isNull(orgUnit) ? null : orgUnit.toUpperCase().trim(); - this.category = Objects.isNull(category) ? null : category.toUpperCase().trim(); + this.orgUnit = new ArrayList<>(); + if (!Objects.isNull(orgUnit)) { + this.orgUnit.addAll(orgUnit.stream() + .map(s -> String.format("'%s'", s.toUpperCase().trim())) + .toList()); + } + this.category = new ArrayList<>(); + if (!Objects.isNull(category)) { + this.category.addAll(category.stream() + .map(s -> String.format("'%s'", s.toUpperCase().trim())) + .toList()); + } this.statusList = new ArrayList<>(); + this.openingIds = new ArrayList<>(); if (!Objects.isNull(statusList)) { this.statusList.addAll(statusList.stream().map(s -> String.format("'%s'", s)).toList()); } @@ -82,6 +94,28 @@ public OpeningSearchFiltersDto( Objects.isNull(mainSearchTerm) ? null : mainSearchTerm.toUpperCase().trim(); } + // Create a constructor with only the List openingIds + public OpeningSearchFiltersDto( + List openingIds) { + this.orgUnit = null; + this.category = null; + this.statusList = new ArrayList<>(); + this.openingIds = openingIds; + this.myOpenings = null; + this.submittedToFrpa = false; + this.disturbanceDateStart = null; + this.disturbanceDateEnd = null; + this.regenDelayDateStart = null; + this.regenDelayDateEnd = null; + this.freeGrowingDateStart = null; + this.freeGrowingDateEnd = null; + this.updateDateStart = null; + this.updateDateEnd = null; + this.cuttingPermitId = null; + this.cutBlockId = null; + this.timberMark = null; + this.mainSearchTerm = null; + } /** * Define if a property has value. * @@ -90,9 +124,10 @@ public OpeningSearchFiltersDto( */ public boolean hasValue(String prop) { return switch (prop) { - case SilvaOracleConstants.ORG_UNIT -> !Objects.isNull(this.orgUnit); - case SilvaOracleConstants.CATEGORY -> !Objects.isNull(this.category); + case SilvaOracleConstants.ORG_UNIT -> !this.orgUnit.isEmpty(); + case SilvaOracleConstants.CATEGORY -> !this.category.isEmpty(); case SilvaOracleConstants.STATUS_LIST -> !this.statusList.isEmpty(); + case SilvaOracleConstants.OPENING_IDS -> !this.openingIds.isEmpty(); case SilvaOracleConstants.MY_OPENINGS -> !Objects.isNull(this.myOpenings); case SilvaOracleConstants.SUBMITTED_TO_FRPA -> !Objects.isNull(this.submittedToFrpa); case SilvaOracleConstants.DISTURBANCE_DATE_START -> diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/dto/OpeningSearchResponseDto.java b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/dto/OpeningSearchResponseDto.java index b658b5da..394fff72 100644 --- a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/dto/OpeningSearchResponseDto.java +++ b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/dto/OpeningSearchResponseDto.java @@ -43,4 +43,5 @@ public class OpeningSearchResponseDto { private Boolean submittedToFrpa; private String forestFileId; private Long silvaReliefAppId; + private LocalDateTime lastViewDate; } diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/endpoint/OpeningEndpoint.java b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/endpoint/OpeningEndpoint.java index ed8e804f..0da1ef34 100644 --- a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/endpoint/OpeningEndpoint.java +++ b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/endpoint/OpeningEndpoint.java @@ -1,17 +1,7 @@ package ca.bc.gov.restapi.results.oracle.endpoint; - -import ca.bc.gov.restapi.results.common.pagination.PaginatedResult; -import ca.bc.gov.restapi.results.common.pagination.PaginatedViaQuery; -import ca.bc.gov.restapi.results.common.pagination.PaginationParameters; -import ca.bc.gov.restapi.results.oracle.dto.RecentOpeningDto; import ca.bc.gov.restapi.results.oracle.service.OpeningService; -import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.CrossOrigin; -import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -22,23 +12,4 @@ public class OpeningEndpoint { private final OpeningService openingService; - - /** - * Fetches all recent openings for the home screen. - * - * @param paginationParameters {@link PaginationParameters} parameters - * @return List of {@link RecentOpeningDto} or empty list. - */ - @GetMapping(path = "/recent-openings", produces = MediaType.APPLICATION_JSON_VALUE) - @PaginatedViaQuery - @CrossOrigin(exposedHeaders = "x-opening-source") - public ResponseEntity> getRecentOpenings( - @Valid PaginationParameters paginationParameters) { - PaginatedResult userOpenings = - openingService.getRecentOpeningsCurrentUser(paginationParameters); - - HttpHeaders headers = new HttpHeaders(); - headers.set("x-opening-source", "user-based"); - return ResponseEntity.ok().headers(headers).body(userOpenings); - } } diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/endpoint/OpeningSearchEndpoint.java b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/endpoint/OpeningSearchEndpoint.java index 8a90bdbb..69f316ca 100644 --- a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/endpoint/OpeningSearchEndpoint.java +++ b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/endpoint/OpeningSearchEndpoint.java @@ -63,9 +63,9 @@ public PaginatedResult openingSearch( @RequestParam(value = "mainSearchTerm", required = false) String mainSearchTerm, @RequestParam(value = "orgUnit", required = false) - String orgUnit, + List orgUnit, @RequestParam(value = "category", required = false) - String category, + List category, @RequestParam(value = "statusList", required = false) List statusList, @RequestParam(value = "myOpenings", required = false) diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/repository/OpeningRecentViewRepository.java b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/repository/OpeningRecentViewRepository.java new file mode 100644 index 00000000..a48c5408 --- /dev/null +++ b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/repository/OpeningRecentViewRepository.java @@ -0,0 +1,343 @@ +package ca.bc.gov.restapi.results.oracle.repository; + +import ca.bc.gov.restapi.results.common.SilvaConstants; +import ca.bc.gov.restapi.results.common.pagination.PaginatedResult; +import ca.bc.gov.restapi.results.common.pagination.PaginationParameters; +import ca.bc.gov.restapi.results.oracle.dto.OpeningSearchResponseDto; +import ca.bc.gov.restapi.results.oracle.enums.OpeningCategoryEnum; +import ca.bc.gov.restapi.results.oracle.enums.OpeningStatusEnum; +import ca.bc.gov.restapi.results.oracle.util.PaginationUtil; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.Query; +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +/** This class represents the Openings Search repository database access. */ +@Slf4j +@Component +public class OpeningRecentViewRepository { + + private final EntityManager em; + + public OpeningRecentViewRepository(@Qualifier("oracleEntityManagerFactory") EntityManagerFactory emf) { + this.em = emf.createEntityManager(); + } + + /** + * Search Opening with filters. + * + * @param openingIds List of opening ids to search. + * @param pagination Pagination parameters with pagination settings. + * @return Paginated result with found records, if any. + */ + public PaginatedResult getUserRecentOpenings( + List openingIds, PaginationParameters pagination) { + + final String sqlQuery = createNativeSqlQuery(openingIds); + log.info("Executing search openings query: {}", sqlQuery); + final Query query = setQueryParameters(openingIds, sqlQuery); + + // Limit to 500 records at the database + query.setMaxResults(SilvaConstants.MAX_PAGE_SIZE); + + List result = query.getResultList(); + int lastPage = PaginationUtil.getLastPage(result.size(), pagination.perPage()); + + PaginatedResult paginatedResult = new PaginatedResult<>(); + paginatedResult.setPageIndex(pagination.page()); + paginatedResult.setPerPage(pagination.perPage()); + paginatedResult.setTotalPages(lastPage); + + if (result.isEmpty() || pagination.page() > lastPage) { + log.info("No search openings result for the search given page index and size!"); + paginatedResult.setData(List.of()); + paginatedResult.setTotalPages(result.isEmpty() ? 0 : lastPage); + paginatedResult.setHasNextPage(false); + return paginatedResult; + } + + int startIndex = PaginationUtil.getStartIndex(pagination.page(), pagination.perPage()); + int endIndex = PaginationUtil.getEndIndex(startIndex, pagination.perPage(), result.size()); + + List resultList = + buildResultListDto(result.subList(startIndex, endIndex)); + + paginatedResult.setData(resultList); + paginatedResult.setPerPage(resultList.size()); + paginatedResult.setTotalPages(lastPage); + paginatedResult.setHasNextPage(pagination.page() < lastPage && pagination.page() > 0); + + return paginatedResult; + } + + private List buildResultListDto(List result) { + List resultList = new ArrayList<>(); + + for (Object obj : result) { + int index = 0; + if (obj.getClass().isArray()) { + Object[] row = (Object[]) obj; + OpeningSearchResponseDto searchOpeningDto = new OpeningSearchResponseDto(); + if (row.length > index) { + searchOpeningDto.setOpeningId(getValue(Integer.class, row[index++], "openingId")); + } + + if (row.length > index) { + String openingNumber = getValue(String.class, row[index++], "openingNumber"); + if (!Objects.isNull(openingNumber)) { + searchOpeningDto.setOpeningNumber(openingNumber.trim()); + } + } + + if (row.length > index) { + String category = getValue(String.class, row[index++], "category"); + searchOpeningDto.setCategory(OpeningCategoryEnum.of(category)); + } + + if (row.length > index) { + String status = getValue(String.class, row[index++], "status"); + searchOpeningDto.setStatus(OpeningStatusEnum.of(status)); + } + + if (row.length > index) { + String cuttingPermitId = getValue(String.class, row[index++], "cuttingPermitId"); + searchOpeningDto.setCuttingPermitId(cuttingPermitId); + } + + if (row.length > index) { + String timberMark = getValue(String.class, row[index++], "timberMark"); + searchOpeningDto.setTimberMark(timberMark); + } + + if (row.length > index) { + String cutBlockId = getValue(String.class, row[index++], "cutBlockId"); + searchOpeningDto.setCutBlockId(cutBlockId); + } + + if (row.length > index) { + BigDecimal openingGrossAreaHa = + getValue(BigDecimal.class, row[index++], "openingGrossAreaHa"); + searchOpeningDto.setOpeningGrossAreaHa(openingGrossAreaHa); + } + + if (row.length > index) { + Timestamp startDate = getValue(Timestamp.class, row[index++], "disturbanceStartDate"); + if (!Objects.isNull(startDate)) { + searchOpeningDto.setDisturbanceStartDate(startDate.toLocalDateTime()); + } + } + + if (row.length > index) { + String forestFileId = getValue(String.class, row[index++], "forestFileId"); + searchOpeningDto.setForestFileId(forestFileId); + } + + if (row.length > index) { + String orgUnitCode = getValue(String.class, row[index++], "orgUnitCode"); + searchOpeningDto.setOrgUnitCode(orgUnitCode); + } + + if (row.length > index) { + String orgUnitName = getValue(String.class, row[index++], "orgUnitName"); + searchOpeningDto.setOrgUnitName(orgUnitName); + } + + if (row.length > index) { + String clientNumber = getValue(String.class, row[index++], "clientNumber"); + searchOpeningDto.setClientNumber(clientNumber); + } + + if (row.length > index) { + String clientLocation = getValue(String.class, row[index++], "clientLocation"); + searchOpeningDto.setClientLocation(clientLocation); + } + + if (row.length > index) { + Timestamp regenDelayDate = getValue(Timestamp.class, row[index++], "regenDelayDate"); + if (!Objects.isNull(regenDelayDate)) { + searchOpeningDto.setRegenDelayDate(regenDelayDate.toLocalDateTime()); + } + } + + if (row.length > index) { + Timestamp earlyDate = getValue(Timestamp.class, row[index++], "earlyFreeGrowingDate"); + if (!Objects.isNull(earlyDate)) { + searchOpeningDto.setEarlyFreeGrowingDate(earlyDate.toLocalDateTime()); + } + } + + if (row.length > index) { + Timestamp dateDate = getValue(Timestamp.class, row[index++], "lateFreeGrowingDate"); + if (!Objects.isNull(dateDate)) { + searchOpeningDto.setLateFreeGrowingDate(dateDate.toLocalDateTime()); + } + } + + if (row.length > index) { + Timestamp updateTimestamp = getValue(Timestamp.class, row[index++], "updateTimestamp"); + searchOpeningDto.setUpdateTimestamp(updateTimestamp.toLocalDateTime()); + } + + if (row.length > index) { + String entryUserId = getValue(String.class, row[index++], "entryUserId"); + searchOpeningDto.setEntryUserId(entryUserId); + } + + if (row.length > index) { + BigDecimal silvaReliefAppId = + getValue(BigDecimal.class, row[index++], "submittedToFrpa108"); + boolean submittedApp = silvaReliefAppId.compareTo(BigDecimal.ZERO) > 0; + searchOpeningDto.setSubmittedToFrpa(submittedApp); + if (submittedApp) { + searchOpeningDto.setSilvaReliefAppId(silvaReliefAppId.longValue()); + } + } + + resultList.add(searchOpeningDto); + } + } + + return resultList; + } + + private T getValue(Class clazz, Object obj, String name) { + if (Objects.isNull(obj)) { + log.debug("{} is null", name); + return null; + } + if (clazz.equals(Integer.class) && obj instanceof Integer intVal) { + log.debug("Integer {}={}", name, intVal); + return clazz.cast(obj); + } + if (clazz.equals(String.class) && obj instanceof String strVal) { + log.debug("String {}={}", name, strVal); + return clazz.cast(obj); + } + if (clazz.equals(LocalDateTime.class) && obj instanceof LocalDateTime localDateTime) { + log.debug("LocalDateTime {}={}", name, localDateTime); + return clazz.cast(obj); + } + if (clazz.equals(BigDecimal.class) && obj instanceof BigDecimal bigDecValue) { + log.debug("BigDecimal {}={}", name, bigDecValue); + return clazz.cast(obj); + } + if (clazz.equals(Timestamp.class) && obj instanceof Timestamp timestamp) { + log.debug("Timestamp {}={}", name, timestamp); + return clazz.cast(obj); + } + log.info("Unhandled class {} for {}", obj.getClass().getName(), name); + return null; + } + + private Query setQueryParameters(List openingIds, String nativeQuery) { + Query query = em.createNativeQuery(nativeQuery); + // Binding the openingIds parameters + for (int i = 0; i < openingIds.size(); i++) { + query.setParameter(i + 1, openingIds.get(i)); // 1-based index for parameters + } + return query; + } + + private String createNativeSqlQuery(List openingIds) { + StringBuilder builder = new StringBuilder(); + builder.append("SELECT o.OPENING_ID AS openingId"); + builder.append(",o.OPENING_NUMBER AS openingNumber"); + builder.append(",o.OPEN_CATEGORY_CODE AS category"); + builder.append(",o.OPENING_STATUS_CODE AS status"); + builder.append(",cboa.CUTTING_PERMIT_ID AS cuttingPermitId"); + builder.append(",cboa.TIMBER_MARK AS timberMark"); + builder.append(",cboa.CUT_BLOCK_ID AS cutBlockId"); + builder.append(",cboa.OPENING_GROSS_AREA AS openingGrossArea"); + builder.append(",cboa.DISTURBANCE_START_DATE AS disturbanceStartDate"); + builder.append(",cboa.FOREST_FILE_ID AS forestFileId"); + builder.append(",ou.ORG_UNIT_CODE AS orgUnitCode"); + builder.append(",ou.ORG_UNIT_NAME AS orgUnitName"); + builder.append(",res.CLIENT_NUMBER AS clientNumber"); + builder.append(",res.CLIENT_LOCN_CODE AS clientLocation"); + + String sql; + sql = ",ADD_MONTHS(cboa.DISTURBANCE_START_DATE, (COALESCE(SMRG.LATE_OFFSET_YEARS,0)*12))"; + builder.append(sql).append(" AS regenDelayDate"); + + sql = ",ADD_MONTHS(cboa.DISTURBANCE_START_DATE, (COALESCE(SMFG.EARLY_OFFSET_YEARS,0)*12))"; + builder.append(sql).append(" AS earlyFreeGrowingDate"); + + sql = ",ADD_MONTHS(cboa.DISTURBANCE_START_DATE, (COALESCE(SMFG.LATE_OFFSET_YEARS,0)*12))"; + builder.append(sql).append(" AS lateFreeGrowingDate"); + + builder.append(",o.UPDATE_TIMESTAMP AS updateTimestamp"); + builder.append(",o.ENTRY_USERID AS entryUserId"); + builder.append(",COALESCE(sra.SILV_RELIEF_APPLICATION_ID, 0) AS submittedToFrpa108 "); + builder.append("FROM THE.OPENING o "); + builder.append("LEFT JOIN THE.CUT_BLOCK_OPEN_ADMIN cboa ON (cboa.OPENING_ID = o.OPENING_ID)"); + builder.append("LEFT JOIN THE.ORG_UNIT ou ON (ou.ORG_UNIT_NO = o.ADMIN_DISTRICT_NO)"); + builder.append("LEFT JOIN the.RESULTS_ELECTRONIC_SUBMISSION res ON ("); + builder.append(" res.RESULTS_SUBMISSION_ID = o.RESULTS_SUBMISSION_ID)"); + builder.append("LEFT JOIN THE.CLIENT_ACRONYM ca ON (ca.CLIENT_NUMBER = res.CLIENT_NUMBER) "); + builder.append("LEFT JOIN THE.ACTIVITY_TREATMENT_UNIT atu ON (atu.OPENING_ID = o.OPENING_ID)"); + builder.append("LEFT JOIN THE.SILV_RELIEF_APPLICATION sra ON ("); + builder.append(" sra.ACTIVITY_TREATMENT_UNIT_ID = atu.ACTIVITY_TREATMENT_UNIT_ID"); + builder.append(" AND sra.SILV_RELIEF_APPL_STATUS_CODE = 'APP') "); + builder.append("LEFT JOIN THE.STOCKING_STANDARD_UNIT ssu ON (ssu.OPENING_ID = o.OPENING_ID) "); + builder.append("LEFT JOIN THE.STOCKING_MILESTONE smrg ON ("); + builder.append(" smrg.STOCKING_STANDARD_UNIT_ID = ssu.STOCKING_STANDARD_UNIT_ID"); + builder.append(" AND SMRG.SILV_MILESTONE_TYPE_CODE = 'RG') "); + builder.append("LEFT JOIN THE.STOCKING_MILESTONE smfg ON ("); + builder.append(" smfg.STOCKING_STANDARD_UNIT_ID = ssu.STOCKING_STANDARD_UNIT_ID"); + builder.append(" AND smfg.SILV_MILESTONE_TYPE_CODE = 'FG') "); + builder.append("WHERE 1=1 "); + + if (openingIds != null && !openingIds.isEmpty()) { + builder.append("AND o.OPENING_ID IN ("); + for (int i = 0; i < openingIds.size(); i++) { + builder.append("?"); + if (i < openingIds.size() - 1) { + builder.append(","); + } + } + builder.append(") "); + } + + /* Group by - to avoid duplications */ + builder.append("GROUP BY o.OPENING_ID "); + builder.append(",o.OPENING_NUMBER "); + builder.append(",o.OPEN_CATEGORY_CODE "); + builder.append(",o.OPENING_STATUS_CODE "); + builder.append(",cboa.CUTTING_PERMIT_ID "); + builder.append(",cboa.TIMBER_MARK "); + builder.append(",cboa.CUT_BLOCK_ID "); + builder.append(",cboa.OPENING_GROSS_AREA "); + builder.append(",cboa.DISTURBANCE_START_DATE "); + builder.append(",cboa.FOREST_FILE_ID "); + builder.append(",ou.ORG_UNIT_CODE "); + builder.append(",ou.ORG_UNIT_NAME "); + builder.append(",res.CLIENT_NUMBER "); + builder.append(",res.CLIENT_LOCN_CODE "); + + sql = ",ADD_MONTHS(cboa.DISTURBANCE_START_DATE, (COALESCE(SMRG.LATE_OFFSET_YEARS, 0) * 12)) "; + builder.append(sql); + + sql = ",ADD_MONTHS(cboa.DISTURBANCE_START_DATE, (COALESCE(SMFG.EARLY_OFFSET_YEARS, 0) * 12)) "; + builder.append(sql); + + sql = ",ADD_MONTHS(cboa.DISTURBANCE_START_DATE, (COALESCE(SMFG.LATE_OFFSET_YEARS, 0) * 12)) "; + builder.append(sql); + + builder.append(",o.UPDATE_TIMESTAMP "); + builder.append(",o.ENTRY_USERID "); + builder.append(",COALESCE(sra.SILV_RELIEF_APPLICATION_ID, 0) "); + + // Order by + builder.append("ORDER BY o.OPENING_ID DESC"); + + return builder.toString(); + } +} diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/repository/OpeningSearchRepository.java b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/repository/OpeningSearchRepository.java index 4c79aed8..25848e08 100644 --- a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/repository/OpeningSearchRepository.java +++ b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/repository/OpeningSearchRepository.java @@ -68,8 +68,7 @@ public PaginatedResult searchOpeningQuery( int startIndex = PaginationUtil.getStartIndex(pagination.page(), pagination.perPage()); int endIndex = PaginationUtil.getEndIndex(startIndex, pagination.perPage(), result.size()); - List resultList = - buildResultListDto(result.subList(startIndex, endIndex)); + List resultList = buildResultListDto(result.subList(startIndex, endIndex)); paginatedResult.setData(resultList); paginatedResult.setPerPage(resultList.size()); @@ -124,8 +123,7 @@ private List buildResultListDto(List result) { } if (row.length > index) { - BigDecimal openingGrossAreaHa = - getValue(BigDecimal.class, row[index++], "openingGrossAreaHa"); + BigDecimal openingGrossAreaHa = getValue(BigDecimal.class, row[index++], "openingGrossAreaHa"); searchOpeningDto.setOpeningGrossAreaHa(openingGrossAreaHa); } @@ -193,8 +191,7 @@ private List buildResultListDto(List result) { } if (row.length > index) { - BigDecimal silvaReliefAppId = - getValue(BigDecimal.class, row[index++], "submittedToFrpa108"); + BigDecimal silvaReliefAppId = getValue(BigDecimal.class, row[index++], "submittedToFrpa108"); boolean submittedApp = silvaReliefAppId.compareTo(BigDecimal.ZERO) > 0; searchOpeningDto.setSubmittedToFrpa(submittedApp); if (submittedApp) { @@ -246,7 +243,7 @@ private Query setQueryParameters(OpeningSearchFiltersDto filtersDto, String nati boolean itsNumeric = filtersDto.getMainSearchTerm().replaceAll("[0-9]", "").isEmpty(); if (itsNumeric) { log.info("Setting mainSearchTerm as numeric filter value"); - // Opening id or File id + // Opening id or File id query.setParameter("openingOrFile", filtersDto.getMainSearchTerm()); } else { log.info("Setting mainSearchTerm as non-numeric filter value"); @@ -257,19 +254,26 @@ private Query setQueryParameters(OpeningSearchFiltersDto filtersDto, String nati // 1. Org Unit code if (filtersDto.hasValue(SilvaOracleConstants.ORG_UNIT)) { - log.info("Setting orgUnit filter value"); - query.setParameter("orgUnit", filtersDto.getOrgUnit()); + log.info("Setting orgUnit filter values"); + // No need to set value since the query already dit it. Didn't work set through named param } // 2. Category code if (filtersDto.hasValue(SilvaOracleConstants.CATEGORY)) { - log.info("Setting category filter value"); - query.setParameter("category", filtersDto.getCategory()); + log.info("Setting category filter values"); + // No need to set value since the query already dit it. Didn't work set through named param } // 3. Status list codes if (filtersDto.hasValue(SilvaOracleConstants.STATUS_LIST)) { log.info("Setting statusList filter values"); - // No need to set value since the query already dit it. Didn't work set through named param + // No need to set value since the query already dit it. Didn't work set through + // named param + } + // similarly for openingIds + if (filtersDto.hasValue(SilvaOracleConstants.OPENING_IDS)) { + log.info("Setting openingIds filter values"); + // No need to set value since the query already dit it. Didn't work set through + // named param } // 4. User entry id if (filtersDto.hasValue(SilvaOracleConstants.MY_OPENINGS)) { @@ -390,8 +394,17 @@ private String createNativeSqlQuery(OpeningSearchFiltersDto filtersDto) { builder.append("WHERE 1=1 "); /* Filters */ + + // List of openings from the openingIds of the filterDto object for the recent openings + if (filtersDto.hasValue(SilvaOracleConstants.OPENING_IDS)) { + String openingIds = String.join(",", filtersDto.getOpeningIds()); + log.info("Filter for openingIds detected! openingIds={}", openingIds); + builder.append(String.format("AND o.OPENING_ID IN (%s) ", openingIds)); + } + // 0. Main number filter [opening_id, opening_number, timber_mark, file_id] - // if it's a number, filter by openingId or fileId, otherwise filter by timber mark and opening + // if it's a number, filter by openingId or fileId, otherwise filter by timber + // mark and opening // number if (filtersDto.hasValue(SilvaOracleConstants.MAIN_SEARCH_TERM)) { log.info("Filter mainSearchTerm detected! mainSearchTerm={}", filtersDto.getMainSearchTerm()); @@ -414,13 +427,15 @@ private String createNativeSqlQuery(OpeningSearchFiltersDto filtersDto) { // 1. Org Unit code if (filtersDto.hasValue(SilvaOracleConstants.ORG_UNIT)) { - log.info("Filter orgUnit detected! orgUnit={}", filtersDto.getOrgUnit()); - builder.append("AND ou.ORG_UNIT_CODE = :orgUnit "); + String orgUnits = String.join(",", filtersDto.getOrgUnit()); + log.info("Filter orgUnit detected! orgUnit={}", orgUnits); + builder.append(String.format("AND ou.ORG_UNIT_CODE IN (%s) ", orgUnits)); } // 2. Category code if (filtersDto.hasValue(SilvaOracleConstants.CATEGORY)) { - log.info("Filter category detected! category={}", filtersDto.getCategory()); - builder.append("AND o.OPEN_CATEGORY_CODE = :category "); + String categories = String.join(",", filtersDto.getCategory()); + log.info("Filter category detected! statusList={}", categories); + builder.append(String.format("AND o.OPEN_CATEGORY_CODE IN (%s) ", categories)); } // 3. Status code if (filtersDto.hasValue(SilvaOracleConstants.STATUS_LIST)) { @@ -428,6 +443,7 @@ private String createNativeSqlQuery(OpeningSearchFiltersDto filtersDto) { log.info("Filter statusList detected! statusList={}", statuses); builder.append(String.format("AND o.OPENING_STATUS_CODE IN (%s) ", statuses)); } + // 4. My openings if (filtersDto.hasValue(SilvaOracleConstants.MY_OPENINGS)) { log.info("Filter myOpenings detected! entryUserId={}", filtersDto.getRequestUserId()); diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/service/OpeningService.java b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/service/OpeningService.java index 967535cc..e92be6e4 100644 --- a/backend/src/main/java/ca/bc/gov/restapi/results/oracle/service/OpeningService.java +++ b/backend/src/main/java/ca/bc/gov/restapi/results/oracle/service/OpeningService.java @@ -50,56 +50,7 @@ public class OpeningService { private final ForestClientApiProvider forestClientApiProvider; - /** - * Gets all recent openings for the Home Screen. - * - * @param pagination A {@link PaginationParameters} with pagination settings. - * @return {@link List} of {@link RecentOpeningDto} containing all recent openings for that user. - */ - public PaginatedResult getRecentOpeningsCurrentUser( - PaginationParameters pagination) { - log.info( - "Getting recent openings to logged user with page index {} and page size {}", - pagination.page(), - pagination.perPage()); - - if (pagination.perPage() > SilvaConstants.MAX_PAGE_SIZE) { - throw new MaxPageSizeException(SilvaConstants.MAX_PAGE_SIZE); - } - - String entryUserId = loggedUserService.getLoggedUserId(); - - // Openings - Pageable pageable = - PageRequest.of( - pagination.page(), pagination.perPage(), Sort.by("updateTimestamp").descending()); - Page openingPage = openingRepository.findAllByEntryUserId(entryUserId, pageable); - - PaginatedResult paginatedResult = new PaginatedResult<>(); - paginatedResult.setPageIndex(pagination.page()); - paginatedResult.setPerPage(pagination.perPage()); - - if (openingPage.getContent().isEmpty()) { - log.info("No recent openings for this user given page index and size!"); - paginatedResult.setData(List.of()); - paginatedResult.setTotalPages(0); - paginatedResult.setHasNextPage(false); - return paginatedResult; - } - - // Cut Block Open Admin - List openingIds = openingPage.getContent().stream().map(OpeningEntity::getId).toList(); - List cutBlocks = - cutBlockOpenAdminService.findAllByOpeningIdIn(openingIds); - - List list = createDtoFromEntity(openingPage.getContent(), cutBlocks); - paginatedResult.setData(list); - paginatedResult.setTotalPages(openingPage.getTotalPages()); - paginatedResult.setHasNextPage(openingPage.hasNext()); - - return paginatedResult; - } - + /** * Get recent openings given the opening creation date. * diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/postgres/dto/UserRecentOpeningDto.java b/backend/src/main/java/ca/bc/gov/restapi/results/postgres/dto/UserRecentOpeningDto.java new file mode 100644 index 00000000..92855357 --- /dev/null +++ b/backend/src/main/java/ca/bc/gov/restapi/results/postgres/dto/UserRecentOpeningDto.java @@ -0,0 +1,15 @@ +package ca.bc.gov.restapi.results.postgres.dto; + +import java.time.LocalDateTime; +import lombok.Builder; +import lombok.With; + +@With +@Builder +public record UserRecentOpeningDto( + String userId, + String openingId, + LocalDateTime lastViewed +) { + +} diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/postgres/endpoint/OpeningFavoriteEndpoint.java b/backend/src/main/java/ca/bc/gov/restapi/results/postgres/endpoint/OpeningFavoriteEndpoint.java new file mode 100644 index 00000000..d766e9f0 --- /dev/null +++ b/backend/src/main/java/ca/bc/gov/restapi/results/postgres/endpoint/OpeningFavoriteEndpoint.java @@ -0,0 +1,40 @@ +package ca.bc.gov.restapi.results.postgres.endpoint; + +import ca.bc.gov.restapi.results.postgres.service.UserOpeningService; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping(path = "/api/openings/favorites", produces = MediaType.APPLICATION_JSON_VALUE) +@RequiredArgsConstructor +public class OpeningFavoriteEndpoint { + + private final UserOpeningService userOpeningService; + + @GetMapping + public List getFavorites() { + return userOpeningService.listUserFavoriteOpenings(); + } + + @PutMapping("/{id}") + @ResponseStatus(HttpStatus.ACCEPTED) + public void addToFavorites(@PathVariable Long id) { + userOpeningService.addUserFavoriteOpening(id); + } + + @DeleteMapping("/{id}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void removeFromFavorites(@PathVariable Long id) { + userOpeningService.removeUserFavoriteOpening(id); + } + +} diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/postgres/endpoint/UserOpeningEndpoint.java b/backend/src/main/java/ca/bc/gov/restapi/results/postgres/endpoint/UserOpeningEndpoint.java index c1bb619f..3b519f43 100644 --- a/backend/src/main/java/ca/bc/gov/restapi/results/postgres/endpoint/UserOpeningEndpoint.java +++ b/backend/src/main/java/ca/bc/gov/restapi/results/postgres/endpoint/UserOpeningEndpoint.java @@ -45,8 +45,8 @@ public ResponseEntity> getUserTrackedOpenings() * @return HTTP status code 201 if success, no response body. */ @PostMapping("/{id}") - public ResponseEntity saveUserOpening(Long id) { - userOpeningService.saveOpeningToUser(id); + public ResponseEntity saveUserOpening(@PathVariable Long id) { + userOpeningService.addUserFavoriteOpening(id); return ResponseEntity.status(HttpStatus.CREATED).build(); } @@ -60,7 +60,7 @@ public ResponseEntity saveUserOpening(Long id) { public ResponseEntity deleteUserOpening( @PathVariable Long id) { - userOpeningService.deleteOpeningFromUserFavourite(id); + userOpeningService.removeUserFavoriteOpening(id); return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); } } diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/postgres/endpoint/UserRecentOpeningEndpoint.java b/backend/src/main/java/ca/bc/gov/restapi/results/postgres/endpoint/UserRecentOpeningEndpoint.java new file mode 100644 index 00000000..99a7f92f --- /dev/null +++ b/backend/src/main/java/ca/bc/gov/restapi/results/postgres/endpoint/UserRecentOpeningEndpoint.java @@ -0,0 +1,49 @@ +package ca.bc.gov.restapi.results.postgres.endpoint; + +import ca.bc.gov.restapi.results.common.pagination.PaginatedResult; +import ca.bc.gov.restapi.results.oracle.dto.OpeningSearchResponseDto; +import ca.bc.gov.restapi.results.postgres.dto.UserRecentOpeningDto; +import ca.bc.gov.restapi.results.postgres.service.UserRecentOpeningService; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class UserRecentOpeningEndpoint { + + private final UserRecentOpeningService userRecentOpeningService; + + /** + * Records the opening viewed by the user based on the provided opening ID. + * + * @param openingId The ID of the opening viewed by the user. + * @return A simple confirmation message or the HTTP code 204-No Content. + */ + @PostMapping("api/users/recent/{openingId}") + public ResponseEntity recordUserViewedOpening(@PathVariable String openingId) { + // Store the opening and return the DTO + UserRecentOpeningDto recentOpeningDto = userRecentOpeningService.storeViewedOpening(openingId); + return ResponseEntity.ok(recentOpeningDto); + } + + /** + * Retrieves a list of recent openings viewed by the user, limited by the number of results. + * + * @param limit The maximum number of results to return. + * @return A list of opening IDs viewed by the user. + */ + @GetMapping("api/user/recent-openings") + public ResponseEntity> getUserRecentOpenings(@RequestParam(defaultValue = "10") int limit) { + // Fetch recent openings for the logged-in user with the specified limit + PaginatedResult recentOpenings = userRecentOpeningService.getAllRecentOpeningsForUser(limit); + return ResponseEntity.ok(recentOpenings); + } +} \ No newline at end of file diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/postgres/entity/UserRecentOpeningEntity.java b/backend/src/main/java/ca/bc/gov/restapi/results/postgres/entity/UserRecentOpeningEntity.java new file mode 100644 index 00000000..733a8619 --- /dev/null +++ b/backend/src/main/java/ca/bc/gov/restapi/results/postgres/entity/UserRecentOpeningEntity.java @@ -0,0 +1,38 @@ +package ca.bc.gov.restapi.results.postgres.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.With; + +import java.time.LocalDateTime; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@With +@Builder +@Entity +@Table(schema = "silva", name = "user_recent_openings") +public class UserRecentOpeningEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "user_id", nullable = false) + private String userId; + + @Column(name = "opening_id", nullable = false) + private String openingId; + + @Column(name = "last_viewed", nullable = false) + private LocalDateTime lastViewed; +} diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/postgres/repository/UserRecentOpeningRepository.java b/backend/src/main/java/ca/bc/gov/restapi/results/postgres/repository/UserRecentOpeningRepository.java new file mode 100644 index 00000000..78b2483c --- /dev/null +++ b/backend/src/main/java/ca/bc/gov/restapi/results/postgres/repository/UserRecentOpeningRepository.java @@ -0,0 +1,16 @@ +package ca.bc.gov.restapi.results.postgres.repository; + +import ca.bc.gov.restapi.results.postgres.entity.UserRecentOpeningEntity; +import java.util.List; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface UserRecentOpeningRepository extends JpaRepository { + UserRecentOpeningEntity findByUserIdAndOpeningId(String userId, String openingId); + // Add a method to fetch recent openings for a user with a limit and sorting by last_viewed in descending order + Page findByUserIdOrderByLastViewedDesc(String userId, Pageable pageable); +} diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/postgres/service/UserOpeningService.java b/backend/src/main/java/ca/bc/gov/restapi/results/postgres/service/UserOpeningService.java index 421ca6b5..448892c2 100644 --- a/backend/src/main/java/ca/bc/gov/restapi/results/postgres/service/UserOpeningService.java +++ b/backend/src/main/java/ca/bc/gov/restapi/results/postgres/service/UserOpeningService.java @@ -1,7 +1,14 @@ package ca.bc.gov.restapi.results.postgres.service; +import ca.bc.gov.restapi.results.common.exception.OpeningNotFoundException; +import ca.bc.gov.restapi.results.common.exception.UserFavoriteNotFoundException; import ca.bc.gov.restapi.results.common.exception.UserOpeningNotFoundException; import ca.bc.gov.restapi.results.common.security.LoggedUserService; +import ca.bc.gov.restapi.results.oracle.dto.OpeningSearchResponseDto; +import ca.bc.gov.restapi.results.oracle.entity.OpeningEntity; +import ca.bc.gov.restapi.results.oracle.enums.OpeningCategoryEnum; +import ca.bc.gov.restapi.results.oracle.enums.OpeningStatusEnum; +import ca.bc.gov.restapi.results.oracle.repository.OpeningRepository; import ca.bc.gov.restapi.results.postgres.dto.MyRecentActionsRequestsDto; import ca.bc.gov.restapi.results.postgres.entity.OpeningsActivityEntity; import ca.bc.gov.restapi.results.postgres.entity.UserOpeningEntity; @@ -11,13 +18,14 @@ import jakarta.transaction.Transactional; import java.util.ArrayList; import java.util.List; -import java.util.Optional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.ocpsoft.prettytime.PrettyTime; import org.springframework.stereotype.Service; -/** This class contains methods for handling User favourite Openings. */ +/** + * This class contains methods for handling User favourite Openings. + */ @Slf4j @Service @RequiredArgsConstructor @@ -29,6 +37,8 @@ public class UserOpeningService { private final OpeningsActivityRepository openingsActivityRepository; + private final OpeningRepository openingRepository; + /** * Gets user's tracked Openings. * @@ -78,46 +88,63 @@ public List getUserTrackedOpenings() { return resultList; } - /** - * Saves one or more Openings IDs to an user. - * - * @param openingId The opening ID. - */ - @Transactional - public void saveOpeningToUser(Long openingId) { - log.info("Opening ID to save in the user favourites: {}", openingId); + public List listUserFavoriteOpenings() { + log.info("Loading user favorite openings for {}", loggedUserService.getLoggedUserId()); - final String userId = loggedUserService.getLoggedUserId(); + List userList = userOpeningRepository + .findAllByUserId(loggedUserService.getLoggedUserId()); - UserOpeningEntity entity = new UserOpeningEntity(); - entity.setUserId(userId); - entity.setOpeningId(openingId); + if (userList.isEmpty()) { + log.info("No saved openings for {}", loggedUserService.getLoggedUserId()); + return List.of(); + } - userOpeningRepository.saveAndFlush(entity); - log.info("Opening ID saved in the user's favourites!"); + return + userList + .stream() + .map(UserOpeningEntity::getOpeningId) + .toList(); } - /** - * Deletes one or more user opening from favourite. - * - * @param openingId The opening ID. - */ @Transactional - public void deleteOpeningFromUserFavourite(Long openingId) { - log.info("Opening ID to delete from the user's favourites: {}", openingId); - String userId = loggedUserService.getLoggedUserId(); - - UserOpeningEntityId openingPk = new UserOpeningEntityId(userId, openingId); + public void addUserFavoriteOpening(Long openingId) { + log.info("Adding opening ID {} as favorite for user {}", openingId, + loggedUserService.getLoggedUserId()); - Optional userOpeningsOp = userOpeningRepository.findById(openingPk); - - if (userOpeningsOp.isEmpty()) { - log.info("Opening id {} not found in the user's favourite list!", openingId); - throw new UserOpeningNotFoundException(); + if (openingRepository.findById(openingId).isEmpty()) { + log.info("Opening ID not found: {}", openingId); + throw new OpeningNotFoundException(); } - userOpeningRepository.delete(userOpeningsOp.get()); - userOpeningRepository.flush(); - log.info("Opening ID deleted from the favourites!"); + log.info("Opening ID {} added as favorite for user {}", openingId, + loggedUserService.getLoggedUserId()); + userOpeningRepository.saveAndFlush( + new UserOpeningEntity( + loggedUserService.getLoggedUserId(), + openingId + ) + ); + } + + @Transactional + public void removeUserFavoriteOpening(Long openingId) { + log.info("Removing opening ID {} from the favorites for user {}", openingId, + loggedUserService.getLoggedUserId()); + userOpeningRepository.findById( + new UserOpeningEntityId( + loggedUserService.getLoggedUserId(), + openingId + ) + ).ifPresentOrElse( + userOpening -> { + userOpeningRepository.delete(userOpening); + userOpeningRepository.flush(); + log.info("Opening ID deleted from the favourites!"); + }, + () -> { + log.info("Opening id {} not found in the user's favourite list!", openingId); + throw new UserFavoriteNotFoundException(); + } + ); } } diff --git a/backend/src/main/java/ca/bc/gov/restapi/results/postgres/service/UserRecentOpeningService.java b/backend/src/main/java/ca/bc/gov/restapi/results/postgres/service/UserRecentOpeningService.java new file mode 100644 index 00000000..3068401f --- /dev/null +++ b/backend/src/main/java/ca/bc/gov/restapi/results/postgres/service/UserRecentOpeningService.java @@ -0,0 +1,107 @@ +package ca.bc.gov.restapi.results.postgres.service; + +import ca.bc.gov.restapi.results.common.pagination.PaginatedResult; +import ca.bc.gov.restapi.results.common.pagination.PaginationParameters; +import ca.bc.gov.restapi.results.common.security.LoggedUserService; +import ca.bc.gov.restapi.results.oracle.dto.OpeningSearchFiltersDto; +import ca.bc.gov.restapi.results.oracle.dto.OpeningSearchResponseDto; +import ca.bc.gov.restapi.results.oracle.service.OpeningService; +import ca.bc.gov.restapi.results.postgres.dto.UserRecentOpeningDto; +import ca.bc.gov.restapi.results.postgres.entity.UserRecentOpeningEntity; +import ca.bc.gov.restapi.results.postgres.repository.UserRecentOpeningRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Map; +import java.util.stream.Collectors; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class UserRecentOpeningService { + + private final LoggedUserService loggedUserService; + private final UserRecentOpeningRepository userRecentOpeningRepository; + private final OpeningService openingService; + + /** + * Stores the opening viewed by the user and returns the DTO. + * + * @param openingId The ID of the opening viewed by the user. + * @return A DTO with userId, openingId, and lastViewed timestamp. + */ + public UserRecentOpeningDto storeViewedOpening(String openingId) { + String userId = loggedUserService.getLoggedUserId(); + LocalDateTime lastViewed = LocalDateTime.now(); + + // Verify that the openingId String contains numbers only and no spaces + if (!openingId.matches("^[0-9]*$")) { + throw new IllegalArgumentException("Opening ID must contain numbers only!"); + } + + // Check if the user has already viewed this opening + UserRecentOpeningEntity existingEntity = userRecentOpeningRepository.findByUserIdAndOpeningId(userId, openingId); + + if (existingEntity != null) { + // Update the last viewed timestamp for the existing record + existingEntity.setLastViewed(lastViewed); + userRecentOpeningRepository.save(existingEntity); // Save the updated entity + } else { + // Create a new entity if this openingId is being viewed for the first time + UserRecentOpeningEntity newEntity = new UserRecentOpeningEntity(null, userId, openingId, lastViewed); + userRecentOpeningRepository.save(newEntity); // Save the new entity + } + + // Return the DTO + return new UserRecentOpeningDto(userId, openingId, lastViewed); + } + + /** + * Retrieves the recent openings viewed by the logged-in user, limited by the provided limit. + * + * @param limit The maximum number of recent openings to retrieve. + * @return A list of opening IDs the user has viewed, sorted by last viewed in descending order. + */ + public PaginatedResult getAllRecentOpeningsForUser(int limit) { + String userId = loggedUserService.getLoggedUserId(); + Pageable pageable = PageRequest.of(0, limit); // PageRequest object to apply limit + + // Fetch recent openings for the user + Page recentOpenings = userRecentOpeningRepository + .findByUserIdOrderByLastViewedDesc(userId, pageable); + + // Extract opening IDs as String + Map openingIds = recentOpenings.getContent().stream() + //.map(opening -> String.valueOf(opening.getOpeningId())) // Convert Integer to String + //.collect(Collectors.toList()); + .collect(Collectors.toMap(UserRecentOpeningEntity::getOpeningId, UserRecentOpeningEntity::getLastViewed)); + log.info("User with the userId {} has the following openindIds {}", userId, openingIds); + if (openingIds.isEmpty()) { + return new PaginatedResult<>(); + } + // Call the oracle service method to fetch opening details for the given opening IDs + //convert the openingIds to a list of strings and pass it to the OpeningSearchFiltersDto constructor + OpeningSearchFiltersDto filtersDto = new OpeningSearchFiltersDto(new ArrayList<>(openingIds.keySet())); + PaginationParameters paginationParameters = new PaginationParameters(0, 10); + PaginatedResult pageResult = openingService.openingSearch(filtersDto, paginationParameters); + // perform the sorting and set the lastViewDate to the OpeningSearchResponseDto + pageResult.setData( + pageResult + .getData() + .stream() + .peek(result -> result.setLastViewDate(openingIds.get(result.getOpeningId().toString()))) + .sorted(Comparator.comparing(OpeningSearchResponseDto::getLastViewDate).reversed()) + .collect(Collectors.toList()) + ); + return pageResult; + } + +} diff --git a/backend/src/main/resources/db/migration/V1__create_schema.sql b/backend/src/main/resources/db/migration/V1__create_schema.sql index d40f9e76..d3e22f45 100644 --- a/backend/src/main/resources/db/migration/V1__create_schema.sql +++ b/backend/src/main/resources/db/migration/V1__create_schema.sql @@ -44,3 +44,19 @@ CREATE TABLE IF NOT EXISTS silva.oracle_extraction_logs ( CONSTRAINT oracle_extraction_logs_pk PRIMARY KEY(id) ); + +-- Create sequence if it doesn't exist +CREATE SEQUENCE IF NOT EXISTS silva.user_recent_openings_seq +START WITH 1 +INCREMENT BY 1 +MINVALUE 1 +NO MAXVALUE +CACHE 30; + +-- Use the sequence in your table creation or insert statements +CREATE TABLE IF NOT EXISTS silva.user_recent_openings ( + id BIGINT PRIMARY KEY DEFAULT nextval('silva.user_recent_openings_seq'), + opening_id VARCHAR(255) NOT NULL, + user_id VARCHAR(255) NOT NULL, + last_viewed TIMESTAMP DEFAULT NOW() +); diff --git a/backend/src/test/java/ca/bc/gov/restapi/results/oracle/endpoint/OpeningEndpointTest.java b/backend/src/test/java/ca/bc/gov/restapi/results/oracle/endpoint/OpeningEndpointTest.java index 6a57c076..f0e4b06a 100644 --- a/backend/src/test/java/ca/bc/gov/restapi/results/oracle/endpoint/OpeningEndpointTest.java +++ b/backend/src/test/java/ca/bc/gov/restapi/results/oracle/endpoint/OpeningEndpointTest.java @@ -64,7 +64,6 @@ void getRecentOpenings_fetchPaginated_shouldSucceed() throws Exception { PaginationParameters params = new PaginationParameters(0, 5); - when(openingService.getRecentOpeningsCurrentUser(params)).thenReturn(paginatedResult); mockMvc .perform( diff --git a/backend/src/test/java/ca/bc/gov/restapi/results/oracle/repository/OpeningSearchRepositoryTest.java b/backend/src/test/java/ca/bc/gov/restapi/results/oracle/repository/OpeningSearchRepositoryTest.java index 2e1c6a0f..6a629696 100644 --- a/backend/src/test/java/ca/bc/gov/restapi/results/oracle/repository/OpeningSearchRepositoryTest.java +++ b/backend/src/test/java/ca/bc/gov/restapi/results/oracle/repository/OpeningSearchRepositoryTest.java @@ -42,8 +42,8 @@ class OpeningSearchRepositoryTest { private OpeningSearchRepository openingSearchRepository; private OpeningSearchFiltersDto mockFilter( - String orgUnit, - String category, + List orgUnit, + List category, List statusList, Boolean myOpenings, Boolean submittedToFrpa, @@ -79,7 +79,7 @@ private OpeningSearchFiltersDto mockFilter( mainSearchTerm); } - private OpeningSearchFiltersDto mockOrgUnit(String orgUnit) { + private OpeningSearchFiltersDto mockOrgUnit(List orgUnit) { return mockFilter( orgUnit, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null); @@ -87,8 +87,8 @@ private OpeningSearchFiltersDto mockOrgUnit(String orgUnit) { private OpeningSearchFiltersDto mockAllFilters() { return mockFilter( - "DCR", - "FTML", + List.of("DCR"), + List.of("FTML"), List.of("APP"), true, false, @@ -481,7 +481,7 @@ void searchOpeningQuery_mainFilterString_shouldSucceed() { @Test @DisplayName("Search opening query org unit filter should succeed") void searchOpeningQuery_orgUnitFilter_shouldSucceed() { - OpeningSearchFiltersDto filters = mockOrgUnit("DCR"); + OpeningSearchFiltersDto filters = mockOrgUnit(List.of("DCR")); PaginationParameters pagination = new PaginationParameters(0, 10); @@ -575,7 +575,7 @@ void searchOpeningQuery_allFilters_shouldSucceed() { Integer openingId = 123456789; String openingNumber = "589"; - OpeningCategoryEnum category = OpeningCategoryEnum.of(filters.getCategory()); + OpeningCategoryEnum category = OpeningCategoryEnum.of("FTML"); OpeningStatusEnum status = OpeningStatusEnum.of(filters.getStatusList().get(0)); String cuttingPermitId = "123"; String timberMark = "EM2184"; @@ -583,7 +583,7 @@ void searchOpeningQuery_allFilters_shouldSucceed() { BigDecimal openingGrossArea = new BigDecimal("11"); Timestamp disturbanceStartDate = Timestamp.valueOf(LocalDateTime.now()); String forestFileId = "TFL47"; - String orgUnitCode = filters.getOrgUnit(); + String orgUnitCode = "DCR"; String orgUnitName = "Org Name"; String clientNumber = "00012797"; String clientLocation = "00"; @@ -655,7 +655,7 @@ void searchOpeningQuery_allFilters_shouldSucceed() { @Test @DisplayName("Search opening query no records found should succeed") void searchOpeningQuery_noRecordsFound_shouldSucceed() { - OpeningSearchFiltersDto filters = mockOrgUnit("AAA"); + OpeningSearchFiltersDto filters = mockOrgUnit(List.of("AAA")); PaginationParameters pagination = new PaginationParameters(0, 10); diff --git a/backend/src/test/java/ca/bc/gov/restapi/results/oracle/service/OpeningServiceTest.java b/backend/src/test/java/ca/bc/gov/restapi/results/oracle/service/OpeningServiceTest.java index 33dbe06f..055103f2 100644 --- a/backend/src/test/java/ca/bc/gov/restapi/results/oracle/service/OpeningServiceTest.java +++ b/backend/src/test/java/ca/bc/gov/restapi/results/oracle/service/OpeningServiceTest.java @@ -16,6 +16,8 @@ import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import java.math.BigDecimal; import java.time.LocalDateTime; +import java.util.List; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -42,53 +44,6 @@ class OpeningServiceTest extends AbstractTestContainerIntegrationTest { @Autowired private OpeningService openingService; - @Test - @DisplayName("Get a list of recent openings for logged user") - void getRecentOpenings_fetchPaginated_shouldSucceed() { - int pages = 3; - int currentPage = 0; - - PaginationParameters pagination = new PaginationParameters(currentPage, pages); - PaginatedResult paginatedResult = - openingService.getRecentOpeningsCurrentUser(pagination); - - Assertions.assertNotNull(paginatedResult); - Assertions.assertEquals(currentPage, paginatedResult.getPageIndex()); - Assertions.assertEquals(1, paginatedResult.getTotalPages()); - Assertions.assertFalse(paginatedResult.getData().isEmpty()); - Assertions.assertEquals(1, paginatedResult.getData().size()); - } - - @Test - @DisplayName("Get an empty list of recent openings for the home screen") - void getRecentOpenings_emptyPages_shouldSucceed() { - - int currentPage = 0; - int pages = 1; - PaginationParameters pagination = new PaginationParameters(currentPage, pages); - PaginatedResult paginatedResult = - openingService.getRecentOpeningsCurrentUser(pagination); - - Assertions.assertNotNull(paginatedResult); - Assertions.assertEquals(currentPage, paginatedResult.getPageIndex()); - Assertions.assertEquals(1, paginatedResult.getTotalPages()); - Assertions.assertFalse(paginatedResult.getData().isEmpty()); - } - - @Test - @DisplayName("Get a list of recent openings without user") - void getRecentOpenings_fetchNoUserPaginated_shouldSucceed() { - - PaginatedResult paginatedResult = - openingService.getRecentOpenings(new PaginationParameters(1, 1)); - - Assertions.assertNotNull(paginatedResult); - Assertions.assertEquals(1, paginatedResult.getPageIndex()); - Assertions.assertEquals(3, paginatedResult.getTotalPages()); - Assertions.assertFalse(paginatedResult.getData().isEmpty()); - Assertions.assertEquals(1, paginatedResult.getData().size()); - } - @Test @DisplayName("Opening search file id happy path should succeed") void openingSearch_fileId_shouldSucceed() { @@ -153,7 +108,7 @@ void openingSearch_orgUnit_shouldSucceed() { PaginatedResult result = openingService.openingSearch(new OpeningSearchFiltersDto( - "TWO", + List.of("TWO"), null, null, null, diff --git a/backend/src/test/java/ca/bc/gov/restapi/results/postgres/endpoint/OpeningFavoriteEndpointIntegrationTest.java b/backend/src/test/java/ca/bc/gov/restapi/results/postgres/endpoint/OpeningFavoriteEndpointIntegrationTest.java new file mode 100644 index 00000000..3f064214 --- /dev/null +++ b/backend/src/test/java/ca/bc/gov/restapi/results/postgres/endpoint/OpeningFavoriteEndpointIntegrationTest.java @@ -0,0 +1,133 @@ +package ca.bc.gov.restapi.results.postgres.endpoint; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +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 ca.bc.gov.restapi.results.postgres.repository.UserOpeningRepository; +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +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; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +@DisplayName("Integration Test | Favorite Openings Endpoint") +@TestMethodOrder(OrderAnnotation.class) +@WithMockJwt +@AutoConfigureMockMvc +class OpeningFavoriteEndpointIntegrationTest extends AbstractTestContainerIntegrationTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private UserOpeningRepository userOpeningRepository; + + @Test + @Order(1) + @DisplayName("No favorites to begin with") + void shouldBeEmpty() throws Exception { + + mockMvc + .perform( + MockMvcRequestBuilders.get("/api/openings/favorites") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$").isEmpty()); + } + + @Test + @Order(2) + @DisplayName("Should add to favorite") + void shouldAddToFavorite() throws Exception { + mockMvc + .perform( + MockMvcRequestBuilders.put("/api/openings/favorites/{openingId}", 101) + .with(csrf().asHeader()) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isAccepted()) + .andExpect(content().string(StringUtils.EMPTY)); + + assertThat(userOpeningRepository.findAll()) + .isNotNull() + .isNotEmpty() + .hasSize(1); + } + + @Test + @Order(3) + @DisplayName("Should not add to favorite if doesn't exist") + void shouldNotAddIfDoesNotExist() throws Exception { + mockMvc + .perform( + MockMvcRequestBuilders.put("/api/openings/favorites/{openingId}", 987) + .with(csrf().asHeader()) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isNotFound()) + .andExpect(content().string(StringUtils.EMPTY)); + //.andExpect(content().string("UserOpening record(s) not found!")); + } + + @Test + @Order(4) + @DisplayName("Multiple requests to add to favorite should not fail, nor duplicate") + void shouldAddToFavoriteAgain() throws Exception { + shouldAddToFavorite(); + } + + @Test + @Order(5) + @DisplayName("Should see list of favorites") + void shouldBeAbleToSeeOpening() throws Exception { + mockMvc + .perform( + MockMvcRequestBuilders.get("/api/openings/favorites") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.[0]").value(101)); + } + + @Test + @Order(6) + @DisplayName("Should remove from favorite") + void shouldRemoveFromFavorites() throws Exception { + mockMvc + .perform( + MockMvcRequestBuilders.delete("/api/openings/favorites/{openingId}", 101) + .with(csrf().asHeader()) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isNoContent()) + .andExpect(content().string(StringUtils.EMPTY)); + + assertThat(userOpeningRepository.findAll()) + .isNotNull() + .isEmpty(); + } + + @Test + @Order(7) + @DisplayName("Should thrown an error if trying to remove entry that doesn't exist") + void shouldThrownErrorIfNoFavoriteFound() throws Exception { + mockMvc + .perform( + MockMvcRequestBuilders.delete("/api/openings/favorites/{openingId}", 101) + .with(csrf().asHeader()) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isNotFound()) + .andExpect(content().string(StringUtils.EMPTY)); + + } + + +} \ No newline at end of file diff --git a/backend/src/test/java/ca/bc/gov/restapi/results/postgres/service/UserOpeningServiceTest.java b/backend/src/test/java/ca/bc/gov/restapi/results/postgres/service/UserOpeningServiceTest.java index 89bbcc27..eb6a30b5 100644 --- a/backend/src/test/java/ca/bc/gov/restapi/results/postgres/service/UserOpeningServiceTest.java +++ b/backend/src/test/java/ca/bc/gov/restapi/results/postgres/service/UserOpeningServiceTest.java @@ -4,8 +4,11 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.when; +import ca.bc.gov.restapi.results.common.exception.UserFavoriteNotFoundException; import ca.bc.gov.restapi.results.common.exception.UserOpeningNotFoundException; import ca.bc.gov.restapi.results.common.security.LoggedUserService; +import ca.bc.gov.restapi.results.oracle.entity.OpeningEntity; +import ca.bc.gov.restapi.results.oracle.repository.OpeningRepository; import ca.bc.gov.restapi.results.postgres.dto.MyRecentActionsRequestsDto; import ca.bc.gov.restapi.results.postgres.entity.OpeningsActivityEntity; import ca.bc.gov.restapi.results.postgres.entity.UserOpeningEntity; @@ -31,6 +34,8 @@ class UserOpeningServiceTest { @Mock OpeningsActivityRepository openingsActivityRepository; + @Mock OpeningRepository openingRepository; + private UserOpeningService userOpeningService; private static final String USER_ID = "TEST"; @@ -39,7 +44,7 @@ class UserOpeningServiceTest { void setup() { this.userOpeningService = new UserOpeningService( - loggedUserService, userOpeningRepository, openingsActivityRepository); + loggedUserService, userOpeningRepository, openingsActivityRepository,openingRepository); } @Test @@ -90,15 +95,16 @@ void getUserTrackedOpenings_noData_shouldSucceed() { @Test @DisplayName("Save opening to user happy path should succeed") - void saveOpeningToUser_happyPath_shouldSucceed() { + void addUser_FavoriteOpening_happyPath_shouldSucceed() { when(loggedUserService.getLoggedUserId()).thenReturn(USER_ID); + when(openingRepository.findById(any())).thenReturn(Optional.of(new OpeningEntity())); when(userOpeningRepository.saveAndFlush(any())).thenReturn(new UserOpeningEntity()); - userOpeningService.saveOpeningToUser(112233L); + userOpeningService.addUserFavoriteOpening(112233L); } @Test @DisplayName("Delete opening from user's favourite happy path should succeed") - void deleteOpeningFromUserFavourite_happyPath_shouldSucceed() { + void removeUserFavoriteOpening_happyPath_shouldSucceed() { when(loggedUserService.getLoggedUserId()).thenReturn(USER_ID); UserOpeningEntity userEntity = new UserOpeningEntity(); @@ -107,19 +113,19 @@ void deleteOpeningFromUserFavourite_happyPath_shouldSucceed() { doNothing().when(userOpeningRepository).delete(any()); doNothing().when(userOpeningRepository).flush(); - userOpeningService.deleteOpeningFromUserFavourite(112233L); + userOpeningService.removeUserFavoriteOpening(112233L); } @Test @DisplayName("Delete opening from user's favourite not found should fail") - void deleteOpeningFromUserFavourite_notFound_shouldFail() { + void removeUserFavoriteOpening_notFound_shouldFail() { when(loggedUserService.getLoggedUserId()).thenReturn(USER_ID); when(userOpeningRepository.findById(any())).thenReturn(Optional.empty()); Assertions.assertThrows( - UserOpeningNotFoundException.class, + UserFavoriteNotFoundException.class, () -> { - userOpeningService.deleteOpeningFromUserFavourite(112233L); + userOpeningService.removeUserFavoriteOpening(112233L); }); } } diff --git a/backend/src/test/java/ca/bc/gov/restapi/results/postgres/service/UserRecentOpeningServiceTest.java b/backend/src/test/java/ca/bc/gov/restapi/results/postgres/service/UserRecentOpeningServiceTest.java new file mode 100644 index 00000000..ef0d56a1 --- /dev/null +++ b/backend/src/test/java/ca/bc/gov/restapi/results/postgres/service/UserRecentOpeningServiceTest.java @@ -0,0 +1,150 @@ +package ca.bc.gov.restapi.results.postgres.service; + +import ca.bc.gov.restapi.results.common.pagination.PaginatedResult; +import ca.bc.gov.restapi.results.common.pagination.PaginationParameters; +import ca.bc.gov.restapi.results.common.security.LoggedUserService; +import ca.bc.gov.restapi.results.oracle.dto.OpeningSearchFiltersDto; +import ca.bc.gov.restapi.results.oracle.dto.OpeningSearchResponseDto; +import ca.bc.gov.restapi.results.oracle.service.OpeningService; +import ca.bc.gov.restapi.results.postgres.dto.UserRecentOpeningDto; +import ca.bc.gov.restapi.results.postgres.entity.UserRecentOpeningEntity; +import ca.bc.gov.restapi.results.postgres.repository.UserRecentOpeningRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +class UserRecentOpeningServiceTest { + + @Mock + private LoggedUserService loggedUserService; + + @Mock + private UserRecentOpeningRepository userRecentOpeningRepository; + + @Mock + private OpeningService openingService; + + @InjectMocks + private UserRecentOpeningService userRecentOpeningService; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void storeViewedOpening_newOpening_savesEntity() { + String userId = "user123"; + String openingId = "123"; + LocalDateTime lastViewed = LocalDateTime.now(); + + when(loggedUserService.getLoggedUserId()).thenReturn(userId); + when(userRecentOpeningRepository.findByUserIdAndOpeningId(userId, openingId)).thenReturn(null); + + UserRecentOpeningDto result = userRecentOpeningService.storeViewedOpening(openingId); + + assertNotNull(result); + assertEquals(userId, result.userId()); + assertEquals(openingId, result.openingId()); + + verify(userRecentOpeningRepository, times(1)).save(any(UserRecentOpeningEntity.class)); + } + + @Test + void storeViewedOpening_existingOpening_updatesEntity() { + String userId = "user123"; + String openingId = "123"; + LocalDateTime lastViewed = LocalDateTime.now(); + UserRecentOpeningEntity existingEntity = new UserRecentOpeningEntity(1L, userId, openingId, lastViewed.minusDays(1)); + + when(loggedUserService.getLoggedUserId()).thenReturn(userId); + when(userRecentOpeningRepository.findByUserIdAndOpeningId(userId, openingId)).thenReturn(existingEntity); + + UserRecentOpeningDto result = userRecentOpeningService.storeViewedOpening(openingId); + + assertNotNull(result); + assertEquals(userId, result.userId()); + assertEquals(openingId, result.openingId()); + + verify(userRecentOpeningRepository, times(1)).save(existingEntity); + } + + @Test + void storeViewedOpening_invalidOpeningId_throwsException() { + String invalidOpeningId = "abc"; + + Exception exception = assertThrows(IllegalArgumentException.class, () -> { + userRecentOpeningService.storeViewedOpening(invalidOpeningId); + }); + + assertEquals("Opening ID must contain numbers only!", exception.getMessage()); + } + + @Test + void getAllRecentOpeningsForUser_noRecentOpenings_returnsEmptyResult() { + String userId = "idir@jasgrewa"; + int limit = 10; + + // Arrange + when(loggedUserService.getLoggedUserId()).thenReturn(userId); + when(userRecentOpeningRepository.findByUserIdOrderByLastViewedDesc(eq(userId), any(PageRequest.class))) + .thenReturn(Page.empty()); // Mocking an empty page of recent openings + + // Act + PaginatedResult result = userRecentOpeningService.getAllRecentOpeningsForUser(limit); + + // Assert + assertNotNull(result); + + // Check if data is null and assert empty + assertTrue(result.getData() == null || result.getData().isEmpty(), "Data should be empty or null"); + } + + + @Test + void getAllRecentOpeningsForUser_withRecentOpenings_returnsSortedResult() { + String userId = "user123"; + int limit = 10; + LocalDateTime now = LocalDateTime.now(); + + UserRecentOpeningEntity opening1 = new UserRecentOpeningEntity(1L, userId, "123", now.minusDays(2)); + UserRecentOpeningEntity opening2 = new UserRecentOpeningEntity(2L, userId, "456", now.minusDays(1)); + + List openings = List.of(opening1, opening2); + when(loggedUserService.getLoggedUserId()).thenReturn(userId); + when(userRecentOpeningRepository.findByUserIdOrderByLastViewedDesc(eq(userId), any(PageRequest.class))) + .thenReturn(new PageImpl<>(openings)); + + OpeningSearchResponseDto dto1 = new OpeningSearchResponseDto(); + dto1.setOpeningId(123); + + OpeningSearchResponseDto dto2 = new OpeningSearchResponseDto(); + dto2.setOpeningId(456); + + PaginatedResult pageResult = new PaginatedResult<>(); + pageResult.setData(List.of(dto1, dto2)); + + when(openingService.openingSearch(any(OpeningSearchFiltersDto.class), any(PaginationParameters.class))) + .thenReturn(pageResult); + + PaginatedResult result = userRecentOpeningService.getAllRecentOpeningsForUser(limit); + + assertNotNull(result); + assertEquals(2, result.getData().size()); + assertEquals((long) 456L, (long) result.getData().get(0).getOpeningId()); // Most recent first + assertEquals((long) 123L, (long) result.getData().get(1).getOpeningId()); // Least recent last + } +} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 597bbb91..4150ff3d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -18,7 +18,8 @@ "@types/node": "^22.0.0", "@vitejs/plugin-react": "^4.0.4", "@vitejs/plugin-react-swc": "^3.3.2", - "aws-amplify": "^6.7.0", + "amazon-cognito-identity-js": "^6.3.13", + "aws-amplify": "^6.5.0", "axios": "^1.6.8", "jspdf": "^2.5.2", "jspdf-autotable": "^3.8.3", @@ -42,7 +43,7 @@ }, "devDependencies": { "@testing-library/dom": "^10.2.0", - "@testing-library/jest-dom": "^6.4.5", + "@testing-library/jest-dom": "^6.6.2", "@testing-library/react": "^16.0.0", "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.5.12", @@ -91,9 +92,9 @@ } }, "node_modules/@aws-amplify/analytics": { - "version": "7.0.56", - "resolved": "https://registry.npmjs.org/@aws-amplify/analytics/-/analytics-7.0.56.tgz", - "integrity": "sha512-J322vuQ1m9+3aMP7y/JiIYLnHAr/34Qwt2oaQQJ9J4i30ThQFS/ldKjxfvwsfgp4uT0vdv2bb3oSChxcjfqXVw==", + "version": "7.0.50", + "resolved": "https://registry.npmjs.org/@aws-amplify/analytics/-/analytics-7.0.50.tgz", + "integrity": "sha512-pFXzLmepr+TFRjyPR5fWIEWvRO2KoHAOpTYp3FHg4qtBjt9FLUSxdhypgQDBA1m7E0wCKY1b/58ax5UzV3XpKA==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/client-firehose": "3.621.0", @@ -107,25 +108,25 @@ } }, "node_modules/@aws-amplify/api": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@aws-amplify/api/-/api-6.1.1.tgz", - "integrity": "sha512-yLt6WtdZYqQAEv0NfukSc9ESZF0tPcj72o8A7w9WsgZMdG5usDV37hjUXbmEtse4tOy8SF8QW+F5mr+F7HN1DA==", + "version": "6.0.52", + "resolved": "https://registry.npmjs.org/@aws-amplify/api/-/api-6.0.52.tgz", + "integrity": "sha512-y9O8g/LXtjK1VZQbzzs67/hoHvvXtcCYr7By1OxVKp8xBGQiFbAA79UCmeuoVvE+Ck7i77JfPCSTrO+v38UPrw==", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/api-graphql": "4.5.1", - "@aws-amplify/api-rest": "4.0.56", + "@aws-amplify/api-graphql": "4.3.3", + "@aws-amplify/api-rest": "4.0.50", "tslib": "^2.5.0" } }, "node_modules/@aws-amplify/api-graphql": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@aws-amplify/api-graphql/-/api-graphql-4.5.1.tgz", - "integrity": "sha512-HeBUysbXk3fidF0HUDa+QSE3+9wLjGQ3y0bV+nyUQMCaXPrVFzTUPjbYvdTm+wVNxZ+wIOQ0aXrKQylkVWLyhA==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@aws-amplify/api-graphql/-/api-graphql-4.3.3.tgz", + "integrity": "sha512-sXBbT+9l4siJNUhuIG5lAeVQ730zuAz8grBycYRkk/X6HL2w1x8dXR8+F5OphG60F+VgTMNH7nfd9mijrukapw==", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/api-rest": "4.0.56", - "@aws-amplify/core": "6.5.1", - "@aws-amplify/data-schema": "^1.7.0", + "@aws-amplify/api-rest": "4.0.50", + "@aws-amplify/core": "6.4.3", + "@aws-amplify/data-schema": "^1.5.0", "@aws-sdk/types": "3.387.0", "graphql": "15.8.0", "rxjs": "^7.8.1", @@ -133,10 +134,35 @@ "uuid": "^9.0.0" } }, + "node_modules/@aws-amplify/api-graphql/node_modules/@aws-sdk/types": { + "version": "3.387.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.387.0.tgz", + "integrity": "sha512-YTjFabNwjTF+6yl88f0/tWff018qmmgMmjlw45s6sdVKueWxdxV68U7gepNLF2nhaQPZa6FDOBoA51NaviVs0Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^2.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-amplify/api-graphql/node_modules/@smithy/types": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.12.0.tgz", + "integrity": "sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-amplify/api-rest": { - "version": "4.0.56", - "resolved": "https://registry.npmjs.org/@aws-amplify/api-rest/-/api-rest-4.0.56.tgz", - "integrity": "sha512-GjIR26NdLujhNRzyX2bO74HgR/ycjAPSH6ECTEXST7bdLvADJJF/Ya+CU8L0EawcgC76GNPvyP5bCVZcy4Wd1A==", + "version": "4.0.50", + "resolved": "https://registry.npmjs.org/@aws-amplify/api-rest/-/api-rest-4.0.50.tgz", + "integrity": "sha512-0FCzUSJnPPodyd9KLXXqkiN0ByGyRfrQEvFOQoPlzRJ7/+FA9TxXUoCtHhwfnBRLP83lyuQ7fwN/g0cT/jK3hQ==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.5.0" @@ -146,9 +172,9 @@ } }, "node_modules/@aws-amplify/auth": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@aws-amplify/auth/-/auth-6.6.0.tgz", - "integrity": "sha512-bCgYwfzr0b6WqqMuzrMt7Rf9ZtJ9OvQAKcf+MMw5KB6xkAqNzxZickMDYqHRACBGSCXHbkKbOFP0rcTBvg6Ghg==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@aws-amplify/auth/-/auth-6.5.0.tgz", + "integrity": "sha512-bRGpUYZQk+Mh7lajFl/Pxk/9YXV7mdHSAGQhS1YjEV7x1It+UwdSK40EpCQ/cT92jQ/4BQH2dAHg8aZ5nTDkKQ==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.5.0" @@ -158,9 +184,9 @@ } }, "node_modules/@aws-amplify/core": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@aws-amplify/core/-/core-6.5.1.tgz", - "integrity": "sha512-wv2wpF0uh/AXQuAi7m/+ptvxaIuhZr4AAW/C5ESd7Uj2Ry6eWniBZM1ijme5d/m4KoY/1KxpimQC7JQkW2c6nQ==", + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/@aws-amplify/core/-/core-6.4.3.tgz", + "integrity": "sha512-1pZCJo7fbkvHdU9243wrZtN2mer2wWWF3eyf7bNSMuxgy82R9uYVki12ML4D0z34guVX7JpxfQfrW13ToJ9P6Q==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-js": "5.2.0", @@ -173,6 +199,31 @@ "uuid": "^9.0.0" } }, + "node_modules/@aws-amplify/core/node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-amplify/core/node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, "node_modules/@aws-amplify/core/node_modules/@aws-sdk/types": { "version": "3.398.0", "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.398.0.tgz", @@ -198,10 +249,19 @@ "node": ">=14.0.0" } }, + "node_modules/@aws-amplify/core/node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/@aws-amplify/data-schema": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@aws-amplify/data-schema/-/data-schema-1.12.1.tgz", - "integrity": "sha512-whhYNoO7VhYCT7R5+CLg4oOyqrFNb9TIjmKsuuuclAvNqwaJmg0nptKmkjgbgy+DsqbMpwYJA0sxMmvctrceBg==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@aws-amplify/data-schema/-/data-schema-1.8.1.tgz", + "integrity": "sha512-qbi7kxqA9LFVXfrHzbq+ZNMjmtUH0HB+KO0dV/k/ud7v4gCocwR9rRWUOJXCqArL12p1J7s+JMrj+Y4NDW7Ltg==", "license": "Apache-2.0", "dependencies": { "@aws-amplify/data-schema-types": "*", @@ -212,9 +272,9 @@ } }, "node_modules/@aws-amplify/data-schema-types": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@aws-amplify/data-schema-types/-/data-schema-types-1.2.0.tgz", - "integrity": "sha512-1hy2r7jl3hQ5J/CGjhmPhFPcdGSakfme1ZLjlTMJZILfYifZLSlGRKNCelMb3J5N9203hyeT5XDi5yR47JL1TQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@aws-amplify/data-schema-types/-/data-schema-types-1.1.1.tgz", + "integrity": "sha512-WhWEEsztpSSxIY0lJ3Ge5iA4g3PBm66SQmy1fBH1FBq0T+cxUBijifOU8MNwf+tf6lGpArMX0RS54HRVF5fUSA==", "license": "Apache-2.0", "dependencies": { "graphql": "15.8.0", @@ -222,12 +282,12 @@ } }, "node_modules/@aws-amplify/datastore": { - "version": "5.0.58", - "resolved": "https://registry.npmjs.org/@aws-amplify/datastore/-/datastore-5.0.58.tgz", - "integrity": "sha512-+BBAMPO3cR2cF5CunhAtxkfmcsWnBbgXk3/IwCzq8iqCJapFOh7BOsHJH/S4c+9mt+eue+YBmxLV4LA4fQLfww==", + "version": "5.0.52", + "resolved": "https://registry.npmjs.org/@aws-amplify/datastore/-/datastore-5.0.52.tgz", + "integrity": "sha512-ut2ElxtWUZBPSyEvr5AxEb3NHF3CPBgVfYzIOnUGU7J7GfgYTS1fFfOMmA5QQ36/b9LxVO8mQKcYwvejv+8Stg==", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/api": "6.1.1", + "@aws-amplify/api": "6.0.52", "buffer": "4.9.2", "idb": "5.0.6", "immer": "9.0.6", @@ -239,9 +299,9 @@ } }, "node_modules/@aws-amplify/notifications": { - "version": "2.0.56", - "resolved": "https://registry.npmjs.org/@aws-amplify/notifications/-/notifications-2.0.56.tgz", - "integrity": "sha512-QyBNVgEq16KTFsRUVEkVLQzGTXhlP2UQxkXh03hKriP3geE3qv0LMTLuDZPNCzl1LTd3M4IFeuiwCWeCWsXJgw==", + "version": "2.0.50", + "resolved": "https://registry.npmjs.org/@aws-amplify/notifications/-/notifications-2.0.50.tgz", + "integrity": "sha512-g7eBEXbDxTpOrZvuzkX2QP+vYn9TieQQJWVe1Z2jI3MzHjvJsWeF3Nt4zH5BMIEE9b6dGvy4FktudR+Vc4t03A==", "license": "Apache-2.0", "dependencies": { "lodash": "^4.17.21", @@ -252,9 +312,9 @@ } }, "node_modules/@aws-amplify/storage": { - "version": "6.6.14", - "resolved": "https://registry.npmjs.org/@aws-amplify/storage/-/storage-6.6.14.tgz", - "integrity": "sha512-swRQaPIGdjY4o6+tfXPq05HtjCDyuY/yMRiHrxU4od6MJVuMwIQUxxkKzTiDtxwEBmlhGRVnCDksxBoH/nxNFw==", + "version": "6.6.8", + "resolved": "https://registry.npmjs.org/@aws-amplify/storage/-/storage-6.6.8.tgz", + "integrity": "sha512-RATpJR4xWIBp6uE2zYI5Sf19DOAMhfSoHAgpRYzir961cqbxfk5e6CIiccgiFWtpKCOSZ4UuhQ1F8rgDzZSsMQ==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "3.398.0", @@ -306,6 +366,17 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-crypto/crc32/node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, "node_modules/@aws-crypto/sha256-browser": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", @@ -321,7 +392,7 @@ "tslib": "^2.6.2" } }, - "node_modules/@aws-crypto/sha256-js": { + "node_modules/@aws-crypto/sha256-browser/node_modules/@aws-crypto/sha256-js": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", @@ -335,6 +406,34 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-1.2.2.tgz", + "integrity": "sha512-Nr1QJIbW/afYYGzYvrF70LtaHrIRtd4TNAglX8BvlfxJLZ45SAmueIKYl5tWoNBPzp65ymXGFK0Bb1vZUpuc9g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^1.2.2", + "@aws-sdk/types": "^3.1.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, "node_modules/@aws-crypto/supports-web-crypto": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", @@ -345,16 +444,22 @@ } }, "node_modules/@aws-crypto/util": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", - "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-1.2.2.tgz", + "integrity": "sha512-H8PjG5WJ4wz0UXAFXeJjWCW1vkvIJ3qUUD+rGRwJ2/hj+xT58Qle2MTql/2MGzkU+1JLAFuR6aJpLAjHwhmwwg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.222.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" + "@aws-sdk/types": "^3.1.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" } }, + "node_modules/@aws-crypto/util/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, "node_modules/@aws-sdk/client-firehose": { "version": "3.621.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-firehose/-/client-firehose-3.621.0.tgz", @@ -407,6 +512,57 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-sdk/client-firehose/node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-firehose/node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-firehose/node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-firehose/node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/client-firehose/node_modules/@aws-sdk/types": { "version": "3.609.0", "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", @@ -420,6 +576,18 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-sdk/client-firehose/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/client-firehose/node_modules/@smithy/util-utf8": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", @@ -489,6 +657,57 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-sdk/client-kinesis/node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-kinesis/node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-kinesis/node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-kinesis/node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/client-kinesis/node_modules/@aws-sdk/types": { "version": "3.609.0", "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", @@ -502,6 +721,18 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-sdk/client-kinesis/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/client-kinesis/node_modules/@smithy/util-utf8": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", @@ -567,6 +798,57 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-sdk/client-personalize-events/node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-personalize-events/node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-personalize-events/node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-personalize-events/node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/client-personalize-events/node_modules/@aws-sdk/types": { "version": "3.609.0", "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", @@ -580,6 +862,18 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-sdk/client-personalize-events/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/client-personalize-events/node_modules/@smithy/util-utf8": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", @@ -695,6 +989,57 @@ "@aws-sdk/client-sts": "^3.621.0" } }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/types": { "version": "3.609.0", "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", @@ -708,6 +1053,18 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-utf8": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", @@ -721,6 +1078,57 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-sdk/client-sso/node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sso/node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/types": { "version": "3.609.0", "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", @@ -734,6 +1142,18 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-sdk/client-sso/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-utf8": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", @@ -795,7 +1215,58 @@ "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" } }, "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/types": { @@ -811,6 +1282,18 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-utf8": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", @@ -1267,28 +1750,16 @@ } }, "node_modules/@aws-sdk/types": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.387.0.tgz", - "integrity": "sha512-YTjFabNwjTF+6yl88f0/tWff018qmmgMmjlw45s6sdVKueWxdxV68U7gepNLF2nhaQPZa6FDOBoA51NaviVs0Q==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/types/node_modules/@smithy/types": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.12.0.tgz", - "integrity": "sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==", + "version": "3.662.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.662.0.tgz", + "integrity": "sha512-Ff9/KRmIm8iEzodxzISLj4/pB/0hX2nVw1RFeOBC65OuM6nHrAdWHHog/CVx25hS5JPU0uE3h6NlWRaBJ7AV5w==", "license": "Apache-2.0", "dependencies": { + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@aws-sdk/util-endpoints": { @@ -1320,9 +1791,9 @@ } }, "node_modules/@aws-sdk/util-locate-window": { - "version": "3.679.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.679.0.tgz", - "integrity": "sha512-zKTd48/ZWrCplkXpYDABI74rQlbR0DNHs8nH95htfSLj9/mWRSwaGptoxwcihaq/77vi/fl2X3y0a1Bo8bt7RA==", + "version": "3.568.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.568.0.tgz", + "integrity": "sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -1392,14 +1863,22 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.3.1" + } + }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", + "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==", "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "js-tokens": "^4.0.0", + "@babel/highlight": "^7.25.7", "picocolors": "^1.0.0" }, "engines": { @@ -1407,30 +1886,30 @@ } }, "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.25.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.7.tgz", + "integrity": "sha512-9ickoLz+hcXCeh7jrcin+/SLWm+GkxE2kTvoYyp38p4WkdFXfQJxDFGWp/YHjiKLPx06z2A7W8XKuqbReXDzsw==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", - "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.7.tgz", + "integrity": "sha512-yJ474Zv3cwiSOO9nXJuqzvwEeM+chDuQ8GJirw+pZ91sCGCyOZ3dJkVE09fTV0VEVzXyLWhh3G/AolYTPX7Mow==", "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.0", - "@babel/generator": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.0", - "@babel/parser": "^7.26.0", - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.26.0", + "@babel/code-frame": "^7.25.7", + "@babel/generator": "^7.25.7", + "@babel/helper-compilation-targets": "^7.25.7", + "@babel/helper-module-transforms": "^7.25.7", + "@babel/helpers": "^7.25.7", + "@babel/parser": "^7.25.7", + "@babel/template": "^7.25.7", + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -1455,13 +1934,12 @@ } }, "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.25.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.7.tgz", + "integrity": "sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.2", - "@babel/types": "^7.26.0", + "@babel/types": "^7.25.7", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -1471,13 +1949,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", - "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.7.tgz", + "integrity": "sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==", "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.9", - "@babel/helper-validator-option": "^7.25.9", + "@babel/compat-data": "^7.25.7", + "@babel/helper-validator-option": "^7.25.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -1496,27 +1974,28 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.7.tgz", + "integrity": "sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==", "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.7.tgz", + "integrity": "sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==", "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-module-imports": "^7.25.7", + "@babel/helper-simple-access": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", + "@babel/traverse": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1526,61 +2005,160 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", - "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.7.tgz", + "integrity": "sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.7.tgz", + "integrity": "sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==", "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" + }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", + "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", + "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.7.tgz", + "integrity": "sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.7.tgz", + "integrity": "sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz", + "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==", "license": "MIT", "dependencies": { - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" + "@babel/helper-validator-identifier": "^7.25.7", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "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.25.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.7.tgz", + "integrity": "sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==", "license": "MIT", "dependencies": { - "@babel/types": "^7.26.0" + "@babel/types": "^7.25.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -1590,12 +2168,12 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", - "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.7.tgz", + "integrity": "sha512-JD9MUnLbPL0WdVK8AWC7F7tTG2OS6u/AKKnsK+NdRhUiVdnzyR1S3kKQCaRLOiaULvUiqK6Z4JQE635VgtCFeg==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1605,12 +2183,12 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", - "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.7.tgz", + "integrity": "sha512-S/JXG/KrbIY06iyJPKfxr0qRxnhNOdkNXYBl/rmwgDd72cQLH9tEGkDm/yJPGvcSIUoikzfjMios9i+xT/uv9w==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1620,9 +2198,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz", + "integrity": "sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==", "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -1632,30 +2210,30 @@ } }, "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz", + "integrity": "sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/code-frame": "^7.25.7", + "@babel/parser": "^7.25.7", + "@babel/types": "^7.25.7" }, "engines": { "node": ">=6.9.0" } }, "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.25.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.7.tgz", + "integrity": "sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9", + "@babel/code-frame": "^7.25.7", + "@babel/generator": "^7.25.7", + "@babel/parser": "^7.25.7", + "@babel/template": "^7.25.7", + "@babel/types": "^7.25.7", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1664,13 +2242,14 @@ } }, "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.25.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.7.tgz", + "integrity": "sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==", "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", + "to-fast-properties": "^2.0.0" }, "engines": { "node": ">=6.9.0" @@ -1730,9 +2309,9 @@ } }, "node_modules/@carbon/colors": { - "version": "11.28.0", - "resolved": "https://registry.npmjs.org/@carbon/colors/-/colors-11.28.0.tgz", - "integrity": "sha512-zmNJk5Ec453aL3bk9RRSmM+dkVbyVMvUuKd6szJCOWLoqBFBXbd3PDRZO0AZeHp/Iel9sBRf1gUYej1BJ/5+hw==", + "version": "11.27.1", + "resolved": "https://registry.npmjs.org/@carbon/colors/-/colors-11.27.1.tgz", + "integrity": "sha512-Jh2sfnlr7U9yWAs4ErbB4gn3JjIP0FgzSRlc+8mJ5iVuoJ0LJqKy/9nM3V9NwLr1IH9evhO8SMpRkJ20PhpajA==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -1740,9 +2319,9 @@ } }, "node_modules/@carbon/feature-flags": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@carbon/feature-flags/-/feature-flags-0.24.0.tgz", - "integrity": "sha512-GQEeXnfmnAtGVfKHSwJoJUsZ8YXAMKgL1TkJf2cUVuHYFk2WScHvCx7SUTDFJKLalGB+QnZPNuFLZ5oapsuwPw==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@carbon/feature-flags/-/feature-flags-0.23.1.tgz", + "integrity": "sha512-PUpAyPQ/ktG1FLCLs4yJC8zKupHnZa8ApOn/lzgln63bz6nTRIc7NBMhb/T/ulFe1ixdvQnrb6jgn2Nw159+6A==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -1750,20 +2329,20 @@ } }, "node_modules/@carbon/grid": { - "version": "11.29.0", - "resolved": "https://registry.npmjs.org/@carbon/grid/-/grid-11.29.0.tgz", - "integrity": "sha512-SAJhTexN6TjbItcUczOqhzgHBGXLhvUhlTdyqj+wzUH0tqEN8g6gLp+1sn9+rL+kV4obSb/7bdSESZtwQr/tQg==", + "version": "11.28.1", + "resolved": "https://registry.npmjs.org/@carbon/grid/-/grid-11.28.1.tgz", + "integrity": "sha512-dNdJ+lop97SISwr18o2YyJGinR7uSN3O1+fYuMtFuMzF6SgY0QVQm038KrUS6K8ifNDGRXD2//XfZZgtfVTH3Q==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@carbon/layout": "^11.28.0", + "@carbon/layout": "^11.27.1", "@ibm/telemetry-js": "^1.5.0" } }, "node_modules/@carbon/icon-helpers": { - "version": "10.54.0", - "resolved": "https://registry.npmjs.org/@carbon/icon-helpers/-/icon-helpers-10.54.0.tgz", - "integrity": "sha512-IJ6uzwRA/6yvSG6tTCQoKIcGehwZYYqjvLHylILmEwyfB8kWV9VmJu957hfrfbS2rmuCXwmN6kCAnb4WS8FnFw==", + "version": "10.53.1", + "resolved": "https://registry.npmjs.org/@carbon/icon-helpers/-/icon-helpers-10.53.1.tgz", + "integrity": "sha512-xRON96jbFLYClsxi/oaLSwT/Z5AePGY85J8bsAuLl4CzAiFFi8M/edzKEgtUYgRH3WSTQDuVzWsyuCoAx7Pmfw==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -1771,13 +2350,12 @@ } }, "node_modules/@carbon/icons-react": { - "version": "11.52.0", - "resolved": "https://registry.npmjs.org/@carbon/icons-react/-/icons-react-11.52.0.tgz", - "integrity": "sha512-t9fkYEpp7o1Gc7c8WiEszqypbSaxfQLku05h3+IlNRFaUVEwpDMq63tM/RsXvKo7EPYESS6ELKMv7lKCITOBKQ==", + "version": "11.50.1", + "resolved": "https://registry.npmjs.org/@carbon/icons-react/-/icons-react-11.50.1.tgz", + "integrity": "sha512-2gWo/SIaxC6jJtbnJ23NWWz9V616ZCSw9YKU/EjqA2Auaq50AN3IbHaot2XemHvjNzwu1RKBPDPn7O/X0Vijwg==", "hasInstallScript": true, - "license": "Apache-2.0", "dependencies": { - "@carbon/icon-helpers": "^10.54.0", + "@carbon/icon-helpers": "^10.53.1", "@ibm/telemetry-js": "^1.5.0", "prop-types": "^15.7.2" }, @@ -1786,9 +2364,9 @@ } }, "node_modules/@carbon/layout": { - "version": "11.28.0", - "resolved": "https://registry.npmjs.org/@carbon/layout/-/layout-11.28.0.tgz", - "integrity": "sha512-Yl0Dsxs00EgAaCKpZCXgebuf9BwiBK66a1Oiao6D12p3ViciZ4L18mlRgOPBcDlolP2tUtncz48TlfkWC097hQ==", + "version": "11.27.1", + "resolved": "https://registry.npmjs.org/@carbon/layout/-/layout-11.27.1.tgz", + "integrity": "sha512-6f9WPaHFQ7bsHNh2Y3A5PC0DR6jho39PeTqzU1I2MRV2YboWCwARzT0SNYgN+4Me6L5Cm5nOCzJ+STM6QZdgtQ==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -1796,9 +2374,9 @@ } }, "node_modules/@carbon/motion": { - "version": "11.24.0", - "resolved": "https://registry.npmjs.org/@carbon/motion/-/motion-11.24.0.tgz", - "integrity": "sha512-JtsSQ3DgVqZXpOdKthetUi5Tp94jkWffgxgrEylbNYErITNt7PeSF6YTXnqtSldk/dUCBkfD1kXkfH1NAxrr1w==", + "version": "11.23.1", + "resolved": "https://registry.npmjs.org/@carbon/motion/-/motion-11.23.1.tgz", + "integrity": "sha512-+Az2eoYJrQvP0+z5Ep0q/O3h7z06Lt/pl9ZKIvFUgnm/2HSkRAlbfigcZN5B0b/BLZArWhzjBRi4SsUtzoOZsA==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -1806,13 +2384,13 @@ } }, "node_modules/@carbon/pictograms-react": { - "version": "11.68.0", - "resolved": "https://registry.npmjs.org/@carbon/pictograms-react/-/pictograms-react-11.68.0.tgz", - "integrity": "sha512-NjOicI4jrrhGTcU235Vuckw8ptkBONXR7WgdAtCVqtfSkxDamUeUIckNfz+djTSCh7vX0+aFNRUxxr0tl37GjQ==", + "version": "11.67.1", + "resolved": "https://registry.npmjs.org/@carbon/pictograms-react/-/pictograms-react-11.67.1.tgz", + "integrity": "sha512-YTRJMyouZlSDnbKFhbFZcyjSGYOCG0+AYsFacNX9clNOCaTGsiUY9ZDFPIQX/OyKLgu0q0SpMsD+TWAXXfNTow==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@carbon/icon-helpers": "^10.54.0", + "@carbon/icon-helpers": "^10.53.1", "@ibm/telemetry-js": "^1.5.0", "prop-types": "^15.7.2" }, @@ -1821,17 +2399,17 @@ } }, "node_modules/@carbon/react": { - "version": "1.69.0", - "resolved": "https://registry.npmjs.org/@carbon/react/-/react-1.69.0.tgz", - "integrity": "sha512-w28Irg0+finWTtUI91U8mOn8Jd4+iftyN6rTnDlGUl5kGeVvudMbHUPxFN9TFafo/ZNV/WXE1Uxq+ovFo0/3lA==", + "version": "1.67.1", + "resolved": "https://registry.npmjs.org/@carbon/react/-/react-1.67.1.tgz", + "integrity": "sha512-0Pzl4iYMDFMJXWuFapncwWf+QTuO3a10OgDKGyhma5O5n9LPjvuqFhmh0tygCBqzdDnteilEvmsszIp2CL/1fg==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.24.7", - "@carbon/feature-flags": "^0.24.0", - "@carbon/icons-react": "^11.52.0", - "@carbon/layout": "^11.28.0", - "@carbon/styles": "^1.68.0", + "@carbon/feature-flags": "^0.23.1", + "@carbon/icons-react": "^11.50.1", + "@carbon/layout": "^11.27.1", + "@carbon/styles": "^1.66.1", "@floating-ui/react": "^0.26.0", "@ibm/telemetry-js": "^1.5.0", "classnames": "2.5.1", @@ -1857,19 +2435,19 @@ } }, "node_modules/@carbon/styles": { - "version": "1.68.0", - "resolved": "https://registry.npmjs.org/@carbon/styles/-/styles-1.68.0.tgz", - "integrity": "sha512-KzUGmUrsWAb9Ln1CfRc38ELHEf7VgTriWwwOcoAEMhurkwX4Qb5kUi1mQ4DYTaNRfdDr7sj20Mirq0di8dquqA==", + "version": "1.66.1", + "resolved": "https://registry.npmjs.org/@carbon/styles/-/styles-1.66.1.tgz", + "integrity": "sha512-IBWfVpGEVafoCOhvz35pSiikzwDw4/91AOAz2MDmJjyW3qyk+c1L/u94HjSTLUQz1bnbqSUa+Ksxe0FV3oTuBQ==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@carbon/colors": "^11.28.0", - "@carbon/feature-flags": "^0.24.0", - "@carbon/grid": "^11.29.0", - "@carbon/layout": "^11.28.0", - "@carbon/motion": "^11.24.0", - "@carbon/themes": "^11.43.0", - "@carbon/type": "^11.33.0", + "@carbon/colors": "^11.27.1", + "@carbon/feature-flags": "^0.23.1", + "@carbon/grid": "^11.28.1", + "@carbon/layout": "^11.27.1", + "@carbon/motion": "^11.23.1", + "@carbon/themes": "^11.41.1", + "@carbon/type": "^11.32.1", "@ibm/plex": "6.0.0-next.6", "@ibm/telemetry-js": "^1.5.0" }, @@ -1892,28 +2470,28 @@ } }, "node_modules/@carbon/themes": { - "version": "11.43.0", - "resolved": "https://registry.npmjs.org/@carbon/themes/-/themes-11.43.0.tgz", - "integrity": "sha512-iBDxHVn1y7QYKVCeBqMjLzryDl5mUG2C67KQbJqGqCfYMKI8L+dkw6KmeeWUYv8rhRhqZq27mm+AODchXO0zcw==", + "version": "11.41.1", + "resolved": "https://registry.npmjs.org/@carbon/themes/-/themes-11.41.1.tgz", + "integrity": "sha512-EW1xfp4H6x5ZSZPlYfX+l0sD+Un4HWmLYfF61QiIzuub844eymMdfEXgyR6JCWykzZzmHYNB1zWEvLQAflFr0Q==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@carbon/colors": "^11.28.0", - "@carbon/layout": "^11.28.0", - "@carbon/type": "^11.33.0", + "@carbon/colors": "^11.27.1", + "@carbon/layout": "^11.27.1", + "@carbon/type": "^11.32.1", "@ibm/telemetry-js": "^1.5.0", "color": "^4.0.0" } }, "node_modules/@carbon/type": { - "version": "11.33.0", - "resolved": "https://registry.npmjs.org/@carbon/type/-/type-11.33.0.tgz", - "integrity": "sha512-v3lfot0vcHNw6WDe32ap3ewpMGwUqhZ6z56sN11jzngRrWVPFgA9U7NciuoylFw301l2htJuZu0dBS2F4ViCXQ==", + "version": "11.32.1", + "resolved": "https://registry.npmjs.org/@carbon/type/-/type-11.32.1.tgz", + "integrity": "sha512-IaVC6WD3ENxsQ3PdHt9vlerjrlNgZIyIzLakGh4F9RqPBlzqVFF73sMbyQIA4mgdAdmqDDPbaJ+U7wCQVhaZ/A==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@carbon/grid": "^11.29.0", - "@carbon/layout": "^11.28.0", + "@carbon/grid": "^11.28.1", + "@carbon/layout": "^11.27.1", "@ibm/telemetry-js": "^1.5.0" } }, @@ -2311,28 +2889,25 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", - "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.4.3" + "eslint-visitor-keys": "^3.3.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "funding": { - "url": "https://opencollective.com/eslint" - }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", "dev": true, "license": "MIT", "engines": { @@ -2423,9 +2998,9 @@ } }, "node_modules/@floating-ui/dom": { - "version": "1.6.12", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.12.tgz", - "integrity": "sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==", + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.11.tgz", + "integrity": "sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ==", "license": "MIT", "dependencies": { "@floating-ui/core": "^1.6.0", @@ -2433,9 +3008,9 @@ } }, "node_modules/@floating-ui/react": { - "version": "0.26.27", - "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.27.tgz", - "integrity": "sha512-jLP72x0Kr2CgY6eTYi/ra3VA9LOkTo4C+DUTrbFgFOExKy3omYVmwMjNKqxAHdsnyLS96BIDLcO2SlnsNf8KUQ==", + "version": "0.26.24", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.24.tgz", + "integrity": "sha512-2ly0pCkZIGEQUq5H8bBK0XJmc1xIK/RM3tvVzY3GBER7IOD1UgmC2Y2tjj4AuS+TC+vTE1KJv2053290jua0Sw==", "license": "MIT", "dependencies": { "@floating-ui/react-dom": "^2.1.2", @@ -2538,9 +3113,9 @@ } }, "node_modules/@ibm/telemetry-js": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@ibm/telemetry-js/-/telemetry-js-1.8.0.tgz", - "integrity": "sha512-1u/8f5TtDHXWNQe+YfIESesZGX2PmhEfyU0znlyFvATch+xc5fPYjXj2gWKMTmdKsDawqAm/BkJBQjx2CDlZww==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@ibm/telemetry-js/-/telemetry-js-1.6.1.tgz", + "integrity": "sha512-ds45f2bz4qVvJPK84VcSMJTvTZI+qXu6wVlBcy9/hAlmzOcxeX6rh8W0De4H133HPONsZWvs0lV/H2aUcznCxw==", "license": "Apache-2.0", "bin": { "ibmtelemetry": "dist/collect.js" @@ -2759,332 +3334,59 @@ "node_modules/@mapbox/vector-tile": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz", - "integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==", - "license": "BSD-3-Clause", - "peer": true, - "dependencies": { - "@mapbox/point-geometry": "~0.1.0" - } - }, - "node_modules/@mapbox/whoots-js": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz", - "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==", - "license": "ISC", - "peer": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@parcel/watcher": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz", - "integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==", - "license": "MIT", - "dependencies": { - "detect-libc": "^1.0.3", - "is-glob": "^4.0.3", - "micromatch": "^4.0.5", - "node-addon-api": "^7.0.0" - }, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "@parcel/watcher-android-arm64": "2.4.1", - "@parcel/watcher-darwin-arm64": "2.4.1", - "@parcel/watcher-darwin-x64": "2.4.1", - "@parcel/watcher-freebsd-x64": "2.4.1", - "@parcel/watcher-linux-arm-glibc": "2.4.1", - "@parcel/watcher-linux-arm64-glibc": "2.4.1", - "@parcel/watcher-linux-arm64-musl": "2.4.1", - "@parcel/watcher-linux-x64-glibc": "2.4.1", - "@parcel/watcher-linux-x64-musl": "2.4.1", - "@parcel/watcher-win32-arm64": "2.4.1", - "@parcel/watcher-win32-ia32": "2.4.1", - "@parcel/watcher-win32-x64": "2.4.1" - } - }, - "node_modules/@parcel/watcher-android-arm64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz", - "integrity": "sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-darwin-arm64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz", - "integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-darwin-x64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz", - "integrity": "sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-freebsd-x64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz", - "integrity": "sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm-glibc": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz", - "integrity": "sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm64-glibc": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz", - "integrity": "sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==", + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "@mapbox/point-geometry": "~0.1.0" } }, - "node_modules/@parcel/watcher-linux-arm64-musl": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz", - "integrity": "sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "node_modules/@mapbox/whoots-js": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz", + "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==", + "license": "ISC", + "peer": true, "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": ">=6.0.0" } }, - "node_modules/@parcel/watcher-linux-x64-glibc": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz", - "integrity": "sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==", - "cpu": [ - "x64" - ], + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-x64-musl": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz", - "integrity": "sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": ">= 8" } }, - "node_modules/@parcel/watcher-win32-arm64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz", - "integrity": "sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==", - "cpu": [ - "arm64" - ], + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": ">= 8" } }, - "node_modules/@parcel/watcher-win32-ia32": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz", - "integrity": "sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==", - "cpu": [ - "ia32" - ], + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-x64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz", - "integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": ">= 8" } }, "node_modules/@pkgjs/parseargs": { @@ -3136,23 +3438,23 @@ } }, "node_modules/@remix-run/router": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.20.0.tgz", - "integrity": "sha512-mUnk8rPJBI9loFDZ+YzPGdeniYK+FTmRD1TMCz7ev2SNIozyKKpnGgsxO34u6Z4z/t0ITuu7voi/AshfsGsgFg==", + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.2.tgz", + "integrity": "sha512-baiMx18+IMuD1yyvOGaHM9QrVUPGGG0jC+z+IPHnRJWUAUvaKuWKyE8gjDj2rzv3sz9zOGoRSPgeBVHRhZnBlA==", "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/@rollup/pluginutils": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.3.tgz", - "integrity": "sha512-Pnsb6f32CD2W3uCaLZIzDmeFyQ2b8UWMFI7xtwUezpcGBDVDW6y9XgAWIlARiGAo6eNF5FK5aQTr0LFyNyqq5A==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.2.tgz", + "integrity": "sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==", "license": "MIT", "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" + "picomatch": "^2.3.1" }, "engines": { "node": ">=14.0.0" @@ -3166,22 +3468,10 @@ } } }, - "node_modules/@rollup/pluginutils/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.3.tgz", - "integrity": "sha512-ufb2CH2KfBWPJok95frEZZ82LtDl0A6QKTa8MoM+cWwDZvVGl5/jNb79pIhRvAalUu+7LD91VYR0nwRD799HkQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", + "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==", "cpu": [ "arm" ], @@ -3192,9 +3482,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.3.tgz", - "integrity": "sha512-iAHpft/eQk9vkWIV5t22V77d90CRofgR2006UiCjHcHJFVI1E0oBkQIAbz+pLtthFw3hWEmVB4ilxGyBf48i2Q==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz", + "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==", "cpu": [ "arm64" ], @@ -3205,9 +3495,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.3.tgz", - "integrity": "sha512-QPW2YmkWLlvqmOa2OwrfqLJqkHm7kJCIMq9kOz40Zo9Ipi40kf9ONG5Sz76zszrmIZZ4hgRIkez69YnTHgEz1w==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", + "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", "cpu": [ "arm64" ], @@ -3218,9 +3508,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.3.tgz", - "integrity": "sha512-KO0pN5x3+uZm1ZXeIfDqwcvnQ9UEGN8JX5ufhmgH5Lz4ujjZMAnxQygZAVGemFWn+ZZC0FQopruV4lqmGMshow==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz", + "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==", "cpu": [ "x64" ], @@ -3230,36 +3520,10 @@ "darwin" ] }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.24.3.tgz", - "integrity": "sha512-CsC+ZdIiZCZbBI+aRlWpYJMSWvVssPuWqrDy/zi9YfnatKKSLFCe6fjna1grHuo/nVaHG+kiglpRhyBQYRTK4A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.24.3.tgz", - "integrity": "sha512-F0nqiLThcfKvRQhZEzMIXOQG4EeX61im61VYL1jo4eBxv4aZRmpin6crnBJQ/nWnCsjH5F6J3W6Stdm0mBNqBg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.3.tgz", - "integrity": "sha512-KRSFHyE/RdxQ1CSeOIBVIAxStFC/hnBgVcaiCkQaVC+EYDtTe4X7z5tBkFyRoBgUGtB6Xg6t9t2kulnX6wJc6A==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz", + "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==", "cpu": [ "arm" ], @@ -3270,9 +3534,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.3.tgz", - "integrity": "sha512-h6Q8MT+e05zP5BxEKz0vi0DhthLdrNEnspdLzkoFqGwnmOzakEHSlXfVyA4HJ322QtFy7biUAVFPvIDEDQa6rw==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz", + "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==", "cpu": [ "arm" ], @@ -3283,9 +3547,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.3.tgz", - "integrity": "sha512-fKElSyXhXIJ9pqiYRqisfirIo2Z5pTTve5K438URf08fsypXrEkVmShkSfM8GJ1aUyvjakT+fn2W7Czlpd/0FQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz", + "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==", "cpu": [ "arm64" ], @@ -3296,9 +3560,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.3.tgz", - "integrity": "sha512-YlddZSUk8G0px9/+V9PVilVDC6ydMz7WquxozToozSnfFK6wa6ne1ATUjUvjin09jp34p84milxlY5ikueoenw==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz", + "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==", "cpu": [ "arm64" ], @@ -3309,9 +3573,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.3.tgz", - "integrity": "sha512-yNaWw+GAO8JjVx3s3cMeG5Esz1cKVzz8PkTJSfYzE5u7A+NvGmbVFEHP+BikTIyYWuz0+DX9kaA3pH9Sqxp69g==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz", + "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==", "cpu": [ "ppc64" ], @@ -3322,9 +3586,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.3.tgz", - "integrity": "sha512-lWKNQfsbpv14ZCtM/HkjCTm4oWTKTfxPmr7iPfp3AHSqyoTz5AgLemYkWLwOBWc+XxBbrU9SCokZP0WlBZM9lA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz", + "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==", "cpu": [ "riscv64" ], @@ -3335,9 +3599,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.3.tgz", - "integrity": "sha512-HoojGXTC2CgCcq0Woc/dn12wQUlkNyfH0I1ABK4Ni9YXyFQa86Fkt2Q0nqgLfbhkyfQ6003i3qQk9pLh/SpAYw==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz", + "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==", "cpu": [ "s390x" ], @@ -3348,9 +3612,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.3.tgz", - "integrity": "sha512-mnEOh4iE4USSccBOtcrjF5nj+5/zm6NcNhbSEfR3Ot0pxBwvEn5QVUXcuOwwPkapDtGZ6pT02xLoPaNv06w7KQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", + "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", "cpu": [ "x64" ], @@ -3361,9 +3625,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.3.tgz", - "integrity": "sha512-rMTzawBPimBQkG9NKpNHvquIUTQPzrnPxPbCY1Xt+mFkW7pshvyIS5kYgcf74goxXOQk0CP3EoOC1zcEezKXhw==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz", + "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==", "cpu": [ "x64" ], @@ -3374,9 +3638,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.3.tgz", - "integrity": "sha512-2lg1CE305xNvnH3SyiKwPVsTVLCg4TmNCF1z7PSHX2uZY2VbUpdkgAllVoISD7JO7zu+YynpWNSKAtOrX3AiuA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz", + "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==", "cpu": [ "arm64" ], @@ -3387,9 +3651,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.3.tgz", - "integrity": "sha512-9SjYp1sPyxJsPWuhOCX6F4jUMXGbVVd5obVpoVEi8ClZqo52ViZewA6eFz85y8ezuOA+uJMP5A5zo6Oz4S5rVQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz", + "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==", "cpu": [ "ia32" ], @@ -3400,9 +3664,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.3.tgz", - "integrity": "sha512-HGZgRFFYrMrP3TJlq58nR1xy8zHKId25vhmm5S9jETEfDf6xybPxsavFTJaufe2zgOGYJBskGlj49CwtEuFhWQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz", + "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==", "cpu": [ "x64" ], @@ -3427,12 +3691,12 @@ "license": "MIT" }, "node_modules/@smithy/abort-controller": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.6.tgz", - "integrity": "sha512-0XuhuHQlEqbNQZp7QxxrFTdVWdwxch4vjxYgfInF91hZFkPxf9QDrdQka0KfxFMPqLNzSw0b95uGTrLliQUavQ==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.5.tgz", + "integrity": "sha512-DhNPnqTqPoG8aZ5dWkFOgsuY+i0GQ3CI6hMmvCoduNsnU9gUZWZBwGfDQsTTB7NvFPkom1df7jMIJWU90kuXXg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.6.0", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -3440,15 +3704,15 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.10.tgz", - "integrity": "sha512-Uh0Sz9gdUuz538nvkPiyv1DZRX9+D15EKDtnQP5rYVAzM/dnYk3P8cg73jcxyOitPgT3mE3OVj7ky7sibzHWkw==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.9.tgz", + "integrity": "sha512-5d9oBf40qC7n2xUoHmntKLdqsyTMMo/r49+eqSIjJ73eDfEtljAxEhzIQ3bkgXJtR3xiv7YzMT/3FF3ORkjWdg==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.9", - "@smithy/types": "^3.6.0", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.8", + "@smithy/util-middleware": "^3.0.7", "tslib": "^2.6.2" }, "engines": { @@ -3456,17 +3720,19 @@ } }, "node_modules/@smithy/core": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.5.1.tgz", - "integrity": "sha512-DujtuDA7BGEKExJ05W5OdxCoyekcKT3Rhg1ZGeiUWaz2BJIWXjZmsG/DIP4W48GHno7AQwRsaCb8NcBgH3QZpg==", + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.4.7.tgz", + "integrity": "sha512-goqMjX+IoVEnHZjYuzu8xwoZjoteMiLXsPHuXPBkWsGwu0o9c3nTjqkUlP1Ez/V8E501aOU7CJ3INk8mQcW2gw==", "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-serde": "^3.0.8", - "@smithy/protocol-http": "^4.1.5", - "@smithy/types": "^3.6.0", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.22", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.3.6", + "@smithy/types": "^3.5.0", "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-middleware": "^3.0.8", - "@smithy/util-stream": "^3.2.1", + "@smithy/util-middleware": "^3.0.7", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -3488,15 +3754,15 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.5.tgz", - "integrity": "sha512-4FTQGAsuwqTzVMmiRVTn0RR9GrbRfkP0wfu/tXWVHd2LgNpTY0uglQpIScXK4NaEyXbB3JmZt8gfVqO50lP8wg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.4.tgz", + "integrity": "sha512-S9bb0EIokfYEuar4kEbLta+ivlKCWOCFsLZuilkNy9i0uEUEHSi47IFLPaxqqCl+0ftKmcOTHayY5nQhAuq7+w==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.9", - "@smithy/property-provider": "^3.1.8", - "@smithy/types": "^3.6.0", - "@smithy/url-parser": "^3.0.8", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/property-provider": "^3.1.7", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", "tslib": "^2.6.2" }, "engines": { @@ -3504,13 +3770,13 @@ } }, "node_modules/@smithy/eventstream-codec": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-3.1.7.tgz", - "integrity": "sha512-kVSXScIiRN7q+s1x7BrQtZ1Aa9hvvP9FeCqCdBxv37GimIHgBCOnZ5Ip80HLt0DhnAKpiobFdGqTFgbaJNrazA==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-3.1.6.tgz", + "integrity": "sha512-SBiOYPBH+5wOyPS7lfI150ePfGLhnp/eTu5RnV9xvhGvRiKfnl6HzRK9wehBph+il8FxS9KTeadx7Rcmf1GLPQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^3.6.0", + "@smithy/types": "^3.5.0", "@smithy/util-hex-encoding": "^3.0.0", "tslib": "^2.6.2" } @@ -3528,13 +3794,13 @@ } }, "node_modules/@smithy/eventstream-serde-browser": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.11.tgz", - "integrity": "sha512-Pd1Wnq3CQ/v2SxRifDUihvpXzirJYbbtXfEnnLV/z0OGCTx/btVX74P86IgrZkjOydOASBGXdPpupYQI+iO/6A==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.10.tgz", + "integrity": "sha512-1i9aMY6Pl/SmA6NjvidxnfBLHMPzhKu2BP148pEt5VwhMdmXn36PE2kWKGa9Hj8b0XGtCTRucpCncylevCtI7g==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^3.0.10", - "@smithy/types": "^3.6.0", + "@smithy/eventstream-serde-universal": "^3.0.9", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -3542,12 +3808,12 @@ } }, "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.8.tgz", - "integrity": "sha512-zkFIG2i1BLbfoGQnf1qEeMqX0h5qAznzaZmMVNnvPZz9J5AWBPkOMckZWPedGUPcVITacwIdQXoPcdIQq5FRcg==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.7.tgz", + "integrity": "sha512-eVzhGQBPEqXXYHvIUku0jMTxd4gDvenRzUQPTmKVWdRvp9JUCKrbAXGQRYiGxUYq9+cqQckRm0wq3kTWnNtDhw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.6.0", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -3555,13 +3821,13 @@ } }, "node_modules/@smithy/eventstream-serde-node": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.10.tgz", - "integrity": "sha512-hjpU1tIsJ9qpcoZq9zGHBJPBOeBGYt+n8vfhDwnITPhEre6APrvqq/y3XMDEGUT2cWQ4ramNqBPRbx3qn55rhw==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.9.tgz", + "integrity": "sha512-JE0Guqvt0xsmfQ5y1EI342/qtJqznBv8cJqkHZV10PwC8GWGU5KNgFbQnsVCcX+xF+qIqwwfRmeWoJCjuOLmng==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^3.0.10", - "@smithy/types": "^3.6.0", + "@smithy/eventstream-serde-universal": "^3.0.9", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -3569,13 +3835,13 @@ } }, "node_modules/@smithy/eventstream-serde-universal": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.10.tgz", - "integrity": "sha512-ewG1GHbbqsFZ4asaq40KmxCmXO+AFSM1b+DcO2C03dyJj/ZH71CiTg853FSE/3SHK9q3jiYQIFjlGSwfxQ9kww==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.9.tgz", + "integrity": "sha512-bydfgSisfepCufw9kCEnWRxqxJFzX/o8ysXWv+W9F2FIyiaEwZ/D8bBKINbh4ONz3i05QJ1xE7A5OKYvgJsXaw==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-codec": "^3.1.7", - "@smithy/types": "^3.6.0", + "@smithy/eventstream-codec": "^3.1.6", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -3596,12 +3862,12 @@ } }, "node_modules/@smithy/hash-node": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.8.tgz", - "integrity": "sha512-tlNQYbfpWXHimHqrvgo14DrMAgUBua/cNoz9fMYcDmYej7MAmUcjav/QKQbFc3NrcPxeJ7QClER4tWZmfwoPng==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.7.tgz", + "integrity": "sha512-SAGHN+QkrwcHFjfWzs/czX94ZEjPJ0CrWJS3M43WswDXVEuP4AVy9gJ3+AF6JQHZD13bojmuf/Ap/ItDeZ+Qfw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.6.0", + "@smithy/types": "^3.5.0", "@smithy/util-buffer-from": "^3.0.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" @@ -3624,12 +3890,12 @@ } }, "node_modules/@smithy/invalid-dependency": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.8.tgz", - "integrity": "sha512-7Qynk6NWtTQhnGTTZwks++nJhQ1O54Mzi7fz4PqZOiYXb4Z1Flpb2yRvdALoggTS8xjtohWUM+RygOtB30YL3Q==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.7.tgz", + "integrity": "sha512-Bq00GsAhHeYSuZX8Kpu4sbI9agH2BNYnqUmmbTGWOhki9NVsWn2jFr896vvoTMH8KAjNX/ErC/8t5QHuEXG+IA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.6.0", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" } }, @@ -3669,13 +3935,13 @@ } }, "node_modules/@smithy/middleware-content-length": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.10.tgz", - "integrity": "sha512-T4dIdCs1d/+/qMpwhJ1DzOhxCZjZHbHazEPJWdB4GDi2HjIZllVzeBEcdJUN0fomV8DURsgOyrbEUzg3vzTaOg==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.9.tgz", + "integrity": "sha512-t97PidoGElF9hTtLCrof32wfWMqC5g2SEJNxaVH3NjlatuNGsdxXRYO/t+RPnxA15RpYiS0f+zG7FuE2DeGgjA==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^4.1.5", - "@smithy/types": "^3.6.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -3683,18 +3949,17 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.2.1.tgz", - "integrity": "sha512-wWO3xYmFm6WRW8VsEJ5oU6h7aosFXfszlz3Dj176pTij6o21oZnzkCLzShfmRaaCHDkBXWBdO0c4sQAvLFP6zA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.4.tgz", + "integrity": "sha512-/ChcVHekAyzUbyPRI8CzPPLj6y8QRAfJngWcLMgsWxKVzw/RzBV69mSOzJYDD3pRwushA1+5tHtPF8fjmzBnrQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^2.5.1", - "@smithy/middleware-serde": "^3.0.8", - "@smithy/node-config-provider": "^3.1.9", - "@smithy/shared-ini-file-loader": "^3.1.9", - "@smithy/types": "^3.6.0", - "@smithy/url-parser": "^3.0.8", - "@smithy/util-middleware": "^3.0.8", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@smithy/util-middleware": "^3.0.7", "tslib": "^2.6.2" }, "engines": { @@ -3702,18 +3967,18 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "3.0.25", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.25.tgz", - "integrity": "sha512-m1F70cPaMBML4HiTgCw5I+jFNtjgz5z5UdGnUbG37vw6kh4UvizFYjqJGHvicfgKMkDL6mXwyPp5mhZg02g5sg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.9", - "@smithy/protocol-http": "^4.1.5", - "@smithy/service-error-classification": "^3.0.8", - "@smithy/smithy-client": "^3.4.2", - "@smithy/types": "^3.6.0", - "@smithy/util-middleware": "^3.0.8", - "@smithy/util-retry": "^3.0.8", + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.22.tgz", + "integrity": "sha512-svEN7O2Tf7BoaBkPzX/8AE2Bv7p16d9/ulFAD1Gmn5g19iMqNk1WIkMxAY7SpB9/tVtUwKx0NaIsBRl88gumZA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.8", + "@smithy/protocol-http": "^4.1.4", + "@smithy/service-error-classification": "^3.0.7", + "@smithy/smithy-client": "^3.3.6", + "@smithy/types": "^3.5.0", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", "tslib": "^2.6.2", "uuid": "^9.0.1" }, @@ -3722,12 +3987,12 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.8.tgz", - "integrity": "sha512-Xg2jK9Wc/1g/MBMP/EUn2DLspN8LNt+GMe7cgF+Ty3vl+Zvu+VeZU5nmhveU+H8pxyTsjrAkci8NqY6OuvZnjA==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.7.tgz", + "integrity": "sha512-VytaagsQqtH2OugzVTq4qvjkLNbWehHfGcGr0JLJmlDRrNCeZoWkWsSOw1nhS/4hyUUWF/TLGGml4X/OnEep5g==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.6.0", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -3735,12 +4000,12 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.8.tgz", - "integrity": "sha512-d7ZuwvYgp1+3682Nx0MD3D/HtkmZd49N3JUndYWQXfRZrYEnCWYc8BHcNmVsPAp9gKvlurdg/mubE6b/rPS9MA==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.7.tgz", + "integrity": "sha512-EyTbMCdqS1DoeQsO4gI7z2Gzq1MoRFAeS8GkFYIwbedB7Lp5zlLHJdg+56tllIIG5Hnf9ZWX48YKSHlsKvugGA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.6.0", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -3748,14 +4013,14 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.9.tgz", - "integrity": "sha512-qRHoah49QJ71eemjuS/WhUXB+mpNtwHRWQr77J/m40ewBVVwvo52kYAmb7iuaECgGTTcYxHS4Wmewfwy++ueew==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.8.tgz", + "integrity": "sha512-E0rU0DglpeJn5ge64mk8wTGEXcQwmpUTY5Zr7IzTpDLmHKiIamINERNZYrPQjg58Ck236sEKSwRSHA4CwshU6Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^3.1.8", - "@smithy/shared-ini-file-loader": "^3.1.9", - "@smithy/types": "^3.6.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -3763,15 +4028,15 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.2.5.tgz", - "integrity": "sha512-PkOwPNeKdvX/jCpn0A8n9/TyoxjGZB8WVoJmm9YzsnAgggTj4CrjpRHlTQw7dlLZ320n1mY1y+nTRUDViKi/3w==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.2.4.tgz", + "integrity": "sha512-49reY3+JgLMFNm7uTAKBWiKCA6XSvkNp9FqhVmusm2jpVnHORYFeFZ704LShtqWfjZW/nhX+7Iexyb6zQfXYIQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^3.1.6", - "@smithy/protocol-http": "^4.1.5", - "@smithy/querystring-builder": "^3.0.8", - "@smithy/types": "^3.6.0", + "@smithy/abort-controller": "^3.1.5", + "@smithy/protocol-http": "^4.1.4", + "@smithy/querystring-builder": "^3.0.7", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -3779,12 +4044,12 @@ } }, "node_modules/@smithy/property-provider": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.8.tgz", - "integrity": "sha512-ukNUyo6rHmusG64lmkjFeXemwYuKge1BJ8CtpVKmrxQxc6rhUX0vebcptFA9MmrGsnLhwnnqeH83VTU9hwOpjA==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.7.tgz", + "integrity": "sha512-QfzLi1GPMisY7bAM5hOUqBdGYnY5S2JAlr201pghksrQv139f8iiiMalXtjczIP5f6owxFn3MINLNUNvUkgtPw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.6.0", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -3792,12 +4057,12 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.5.tgz", - "integrity": "sha512-hsjtwpIemmCkm3ZV5fd/T0bPIugW1gJXwZ/hpuVubt2hEUApIoUTrf6qIdh9MAWlw0vjMrA1ztJLAwtNaZogvg==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.4.tgz", + "integrity": "sha512-MlWK8eqj0JlpZBnWmjQLqmFp71Ug00P+m72/1xQB3YByXD4zZ+y9N4hYrR0EDmrUCZIkyATWHOXFgtavwGDTzQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.6.0", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -3805,12 +4070,12 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.8.tgz", - "integrity": "sha512-btYxGVqFUARbUrN6VhL9c3dnSviIwBYD9Rz1jHuN1hgh28Fpv2xjU1HeCeDJX68xctz7r4l1PBnFhGg1WBBPuA==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.7.tgz", + "integrity": "sha512-65RXGZZ20rzqqxTsChdqSpbhA6tdt5IFNgG6o7e1lnPVLCe6TNWQq4rTl4N87hTDD8mV4IxJJnvyE7brbnRkQw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.6.0", + "@smithy/types": "^3.5.0", "@smithy/util-uri-escape": "^3.0.0", "tslib": "^2.6.2" }, @@ -3819,12 +4084,12 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.8.tgz", - "integrity": "sha512-BtEk3FG7Ks64GAbt+JnKqwuobJNX8VmFLBsKIwWr1D60T426fGrV2L3YS5siOcUhhp6/Y6yhBw1PSPxA5p7qGg==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.7.tgz", + "integrity": "sha512-Fouw4KJVWqqUVIu1gZW8BH2HakwLz6dvdrAhXeXfeymOBrZw+hcqaWs+cS1AZPVp4nlbeIujYrKA921ZW2WMPA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.6.0", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -3832,24 +4097,24 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.8.tgz", - "integrity": "sha512-uEC/kCCFto83bz5ZzapcrgGqHOh/0r69sZ2ZuHlgoD5kYgXJEThCoTuw/y1Ub3cE7aaKdznb+jD9xRPIfIwD7g==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.7.tgz", + "integrity": "sha512-91PRkTfiBf9hxkIchhRKJfl1rsplRDyBnmyFca3y0Z3x/q0JJN480S83LBd8R6sBCkm2bBbqw2FHp0Mbh+ecSA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.6.0" + "@smithy/types": "^3.5.0" }, "engines": { "node": ">=16.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.9.tgz", - "integrity": "sha512-/+OsJRNtoRbtsX0UpSgWVxFZLsJHo/4sTr+kBg/J78sr7iC+tHeOvOJrS5hCpVQ6sWBbhWLp1UNiuMyZhE6pmA==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.8.tgz", + "integrity": "sha512-0NHdQiSkeGl0ICQKcJQ2lCOKH23Nb0EaAa7RDRId6ZqwXkw4LJyIyZ0t3iusD4bnKYDPLGy2/5e2rfUhrt0Acw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.6.0", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -3857,16 +4122,16 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.2.1.tgz", - "integrity": "sha512-NsV1jF4EvmO5wqmaSzlnTVetemBS3FZHdyc5CExbDljcyJCEEkJr8ANu2JvtNbVg/9MvKAWV44kTrGS+Pi4INg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.2.0.tgz", + "integrity": "sha512-LafbclHNKnsorMgUkKm7Tk7oJ7xizsZ1VwqhGKqoCIrXh4fqDDp73fK99HOEEgcsQbtemmeY/BPv0vTVYYUNEQ==", "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^3.0.0", - "@smithy/protocol-http": "^4.1.5", - "@smithy/types": "^3.6.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-middleware": "^3.0.8", + "@smithy/util-middleware": "^3.0.7", "@smithy/util-uri-escape": "^3.0.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" @@ -3901,17 +4166,16 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.4.2.tgz", - "integrity": "sha512-dxw1BDxJiY9/zI3cBqfVrInij6ShjpV4fmGHesGZZUiP9OSE/EVfdwdRz0PgvkEvrZHpsj2htRaHJfftE8giBA==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.3.6.tgz", + "integrity": "sha512-qdH+mvDHgq1ss6mocyIl2/VjlWXew7pGwZQydwYJczEc22HZyX3k8yVPV9aZsbYbssHPvMDRA5rfBDrjQUbIIw==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^2.5.1", - "@smithy/middleware-endpoint": "^3.2.1", - "@smithy/middleware-stack": "^3.0.8", - "@smithy/protocol-http": "^4.1.5", - "@smithy/types": "^3.6.0", - "@smithy/util-stream": "^3.2.1", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "@smithy/util-stream": "^3.1.9", "tslib": "^2.6.2" }, "engines": { @@ -3919,9 +4183,9 @@ } }, "node_modules/@smithy/types": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.6.0.tgz", - "integrity": "sha512-8VXK/KzOHefoC65yRgCn5vG1cysPJjHnOVt9d0ybFQSmJgQj152vMn4EkYhGuaOmnnZvCPav/KnYyE6/KsNZ2w==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.5.0.tgz", + "integrity": "sha512-QN0twHNfe8mNJdH9unwsCK13GURU7oEAZqkBI+rsvpv1jrmserO+WnLE7jidR9W/1dxwZ0u/CB01mV2Gms/K2Q==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -3931,13 +4195,13 @@ } }, "node_modules/@smithy/url-parser": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.8.tgz", - "integrity": "sha512-4FdOhwpTW7jtSFWm7SpfLGKIBC9ZaTKG5nBF0wK24aoQKQyDIKUw3+KFWCQ9maMzrgTJIuOvOnsV2lLGW5XjTg==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.7.tgz", + "integrity": "sha512-70UbSSR8J97c1rHZOWhl+VKiZDqHWxs/iW8ZHrHp5fCCPLSBE7GcUlUvKSle3Ca+J9LLbYCj/A79BxztBvAfpA==", "license": "Apache-2.0", "dependencies": { - "@smithy/querystring-parser": "^3.0.8", - "@smithy/types": "^3.6.0", + "@smithy/querystring-parser": "^3.0.7", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" } }, @@ -4015,14 +4279,14 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "3.0.25", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.25.tgz", - "integrity": "sha512-fRw7zymjIDt6XxIsLwfJfYUfbGoO9CmCJk6rjJ/X5cd20+d2Is7xjU5Kt/AiDt6hX8DAf5dztmfP5O82gR9emA==", + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.22.tgz", + "integrity": "sha512-WKzUxNsOun5ETwEOrvooXeI1mZ8tjDTOcN4oruELWHhEYDgQYWwxZupURVyovcv+h5DyQT/DzK5nm4ZoR/Tw5Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^3.1.8", - "@smithy/smithy-client": "^3.4.2", - "@smithy/types": "^3.6.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/smithy-client": "^3.3.6", + "@smithy/types": "^3.5.0", "bowser": "^2.11.0", "tslib": "^2.6.2" }, @@ -4031,17 +4295,17 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "3.0.25", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.25.tgz", - "integrity": "sha512-H3BSZdBDiVZGzt8TG51Pd2FvFO0PAx/A0mJ0EH8a13KJ6iUCdYnw/Dk/MdC1kTd0eUuUGisDFaxXVXo4HHFL1g==", + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.22.tgz", + "integrity": "sha512-hUsciOmAq8fsGwqg4+pJfNRmrhfqMH4Y9UeGcgeUl88kPAoYANFATJqCND+O4nUvwp5TzsYwGpqpcBKyA8LUUg==", "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^3.0.10", - "@smithy/credential-provider-imds": "^3.2.5", - "@smithy/node-config-provider": "^3.1.9", - "@smithy/property-provider": "^3.1.8", - "@smithy/smithy-client": "^3.4.2", - "@smithy/types": "^3.6.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/credential-provider-imds": "^3.2.4", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/property-provider": "^3.1.7", + "@smithy/smithy-client": "^3.3.6", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -4049,13 +4313,13 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.4.tgz", - "integrity": "sha512-kPt8j4emm7rdMWQyL0F89o92q10gvCUa6sBkBtDJ7nV2+P7wpXczzOfoDJ49CKXe5CCqb8dc1W+ZdLlrKzSAnQ==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.3.tgz", + "integrity": "sha512-34eACeKov6jZdHqS5hxBMJ4KyWKztTMulhuQ2UdOoP6vVxMLrOKUqIXAwJe/wiWMhXhydLW664B02CNpQBQ4Aw==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.9", - "@smithy/types": "^3.6.0", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -4075,12 +4339,12 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.8.tgz", - "integrity": "sha512-p7iYAPaQjoeM+AKABpYWeDdtwQNxasr4aXQEA/OmbOaug9V0odRVDy3Wx4ci8soljE/JXQo+abV0qZpW8NX0yA==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.7.tgz", + "integrity": "sha512-OVA6fv/3o7TMJTpTgOi1H5OTwnuUa8hzRzhSFDtZyNxi6OZ70L/FHattSmhE212I7b6WSOJAAmbYnvcjTHOJCA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.6.0", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -4088,13 +4352,13 @@ } }, "node_modules/@smithy/util-retry": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.8.tgz", - "integrity": "sha512-TCEhLnY581YJ+g1x0hapPz13JFqzmh/pMWL2KEFASC51qCfw3+Y47MrTmea4bUE5vsdxQ4F6/KFbUeSz22Q1ow==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.7.tgz", + "integrity": "sha512-nh1ZO1vTeo2YX1plFPSe/OXaHkLAHza5jpokNiiKX2M5YpNUv6RxGJZhpfmiR4jSvVHCjIDmILjrxKmP+/Ghug==", "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^3.0.8", - "@smithy/types": "^3.6.0", + "@smithy/service-error-classification": "^3.0.7", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -4102,14 +4366,14 @@ } }, "node_modules/@smithy/util-stream": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.2.1.tgz", - "integrity": "sha512-R3ufuzJRxSJbE58K9AEnL/uSZyVdHzud9wLS8tIbXclxKzoe09CRohj2xV8wpx5tj7ZbiJaKYcutMm1eYgz/0A==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.9.tgz", + "integrity": "sha512-7YAR0Ub3MwTMjDfjnup4qa6W8gygZMxikBhFMPESi6ASsl/rZJhwLpF/0k9TuezScCojsM0FryGdz4LZtjKPPQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^4.0.0", - "@smithy/node-http-handler": "^3.2.5", - "@smithy/types": "^3.6.0", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/types": "^3.5.0", "@smithy/util-base64": "^3.0.0", "@smithy/util-buffer-from": "^3.0.0", "@smithy/util-hex-encoding": "^3.0.0", @@ -4120,19 +4384,6 @@ "node": ">=16.0.0" } }, - "node_modules/@smithy/util-stream/node_modules/@smithy/fetch-http-handler": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.0.0.tgz", - "integrity": "sha512-MLb1f5tbBO2X6K4lMEKJvxeLooyg7guq48C2zKr4qM7F2Gpkz4dc+hdSgu77pCJ76jVqFBjZczHYAs6dp15N+g==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^4.1.5", - "@smithy/querystring-builder": "^3.0.8", - "@smithy/types": "^3.6.0", - "@smithy/util-base64": "^3.0.0", - "tslib": "^2.6.2" - } - }, "node_modules/@smithy/util-stream/node_modules/@smithy/util-hex-encoding": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", @@ -4209,13 +4460,13 @@ } }, "node_modules/@smithy/util-waiter": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.1.7.tgz", - "integrity": "sha512-d5yGlQtmN/z5eoTtIYgkvOw27US2Ous4VycnXatyoImIF9tzlcpnKqQ/V7qhvJmb2p6xZne1NopCLakdTnkBBQ==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.1.6.tgz", + "integrity": "sha512-xs/KAwWOeCklq8aMlnpk25LgxEYHKOEodfjfKclDMLcBJEVEKzDLxZxBQyztcuPJ7F54213NJS8PxoiHNMdItQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^3.1.6", - "@smithy/types": "^3.6.0", + "@smithy/abort-controller": "^3.1.5", + "@smithy/types": "^3.5.0", "tslib": "^2.6.2" }, "engines": { @@ -4436,14 +4687,14 @@ } }, "node_modules/@swc/core": { - "version": "1.7.42", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.42.tgz", - "integrity": "sha512-iQrRk3SKndQZ4ptJv1rzeQSiCYQIhMjiO97QXOlCcCoaazOLKPnLnXzU4Kv0FuBFyYfG2FE94BoR0XI2BN02qw==", + "version": "1.7.26", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.26.tgz", + "integrity": "sha512-f5uYFf+TmMQyYIoxkn/evWhNGuUzC730dFwAKGwBVHHVoPyak1/GvJUm6i1SKl+2Hrj9oN0i3WSoWWZ4pgI8lw==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.3", - "@swc/types": "^0.1.13" + "@swc/types": "^0.1.12" }, "engines": { "node": ">=10" @@ -4453,16 +4704,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.7.42", - "@swc/core-darwin-x64": "1.7.42", - "@swc/core-linux-arm-gnueabihf": "1.7.42", - "@swc/core-linux-arm64-gnu": "1.7.42", - "@swc/core-linux-arm64-musl": "1.7.42", - "@swc/core-linux-x64-gnu": "1.7.42", - "@swc/core-linux-x64-musl": "1.7.42", - "@swc/core-win32-arm64-msvc": "1.7.42", - "@swc/core-win32-ia32-msvc": "1.7.42", - "@swc/core-win32-x64-msvc": "1.7.42" + "@swc/core-darwin-arm64": "1.7.26", + "@swc/core-darwin-x64": "1.7.26", + "@swc/core-linux-arm-gnueabihf": "1.7.26", + "@swc/core-linux-arm64-gnu": "1.7.26", + "@swc/core-linux-arm64-musl": "1.7.26", + "@swc/core-linux-x64-gnu": "1.7.26", + "@swc/core-linux-x64-musl": "1.7.26", + "@swc/core-win32-arm64-msvc": "1.7.26", + "@swc/core-win32-ia32-msvc": "1.7.26", + "@swc/core-win32-x64-msvc": "1.7.26" }, "peerDependencies": { "@swc/helpers": "*" @@ -4474,9 +4725,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.7.42", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.42.tgz", - "integrity": "sha512-fWhaCs2+8GDRIcjExVDEIfbptVrxDqG8oHkESnXgymmvqTWzWei5SOnPNMS8Q+MYsn/b++Y2bDxkcwmq35Bvxg==", + "version": "1.7.26", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.26.tgz", + "integrity": "sha512-FF3CRYTg6a7ZVW4yT9mesxoVVZTrcSWtmZhxKCYJX9brH4CS/7PRPjAKNk6kzWgWuRoglP7hkjQcd6EpMcZEAw==", "cpu": [ "arm64" ], @@ -4490,9 +4741,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.7.42", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.7.42.tgz", - "integrity": "sha512-ZaVHD2bijrlkCyD7NDzLmSK849Jgcx+6DdL4x1dScoz1slJ8GTvLtEu0JOUaaScQwA+cVlhmrmlmi9ssjbRLGQ==", + "version": "1.7.26", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.7.26.tgz", + "integrity": "sha512-az3cibZdsay2HNKmc4bjf62QVukuiMRh5sfM5kHR/JMTrLyS6vSw7Ihs3UTkZjUxkLTT8ro54LI6sV6sUQUbLQ==", "cpu": [ "x64" ], @@ -4506,9 +4757,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.7.42", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.42.tgz", - "integrity": "sha512-iF0BJj7hVTbY/vmbvyzVTh/0W80+Q4fbOYschdUM3Bsud39TA+lSaPOefOHywkNH58EQ1z3EAxYcJOWNES7GFQ==", + "version": "1.7.26", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.26.tgz", + "integrity": "sha512-VYPFVJDO5zT5U3RpCdHE5v1gz4mmR8BfHecUZTmD2v1JeFY6fv9KArJUpjrHEEsjK/ucXkQFmJ0jaiWXmpOV9Q==", "cpu": [ "arm" ], @@ -4522,9 +4773,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.7.42", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.42.tgz", - "integrity": "sha512-xGu8j+DOLYTLkVmsfZPJbNPW1EkiWgSucT0nOlz77bLxImukt/0+HVm2hOwHSKuArQ8C3cjahAMY3b/s4VH2ww==", + "version": "1.7.26", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.26.tgz", + "integrity": "sha512-YKevOV7abpjcAzXrhsl+W48Z9mZvgoVs2eP5nY+uoMAdP2b3GxC0Df1Co0I90o2lkzO4jYBpTMcZlmUXLdXn+Q==", "cpu": [ "arm64" ], @@ -4538,9 +4789,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.7.42", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.42.tgz", - "integrity": "sha512-qtW3JNO7i1yHEko59xxz+jY38+tYmB96JGzj6XzygMbYJYZDYbrOpXQvKbMGNG3YeTDan7Fp2jD0dlKf7NgDPA==", + "version": "1.7.26", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.26.tgz", + "integrity": "sha512-3w8iZICMkQQON0uIcvz7+Q1MPOW6hJ4O5ETjA0LSP/tuKqx30hIniCGOgPDnv3UTMruLUnQbtBwVCZTBKR3Rkg==", "cpu": [ "arm64" ], @@ -4554,9 +4805,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.7.42", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.42.tgz", - "integrity": "sha512-F9WY1TN+hhhtiEzZjRQziNLt36M5YprMeOBHjsLVNqwgflzleSI7ulgnlQECS8c8zESaXj3ksGduAoJYtPC1cA==", + "version": "1.7.26", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.26.tgz", + "integrity": "sha512-c+pp9Zkk2lqb06bNGkR2Looxrs7FtGDMA4/aHjZcCqATgp348hOKH5WPvNLBl+yPrISuWjbKDVn3NgAvfvpH4w==", "cpu": [ "x64" ], @@ -4570,9 +4821,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.7.42", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.42.tgz", - "integrity": "sha512-7YMdOaYKLMQ8JGfnmRDwidpLFs/6ka+80zekeM0iCVO48yLrJR36G0QGXzMjKsXI0BPhq+mboZRRENK4JfQnEA==", + "version": "1.7.26", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.26.tgz", + "integrity": "sha512-PgtyfHBF6xG87dUSSdTJHwZ3/8vWZfNIXQV2GlwEpslrOkGqy+WaiiyE7Of7z9AvDILfBBBcJvJ/r8u980wAfQ==", "cpu": [ "x64" ], @@ -4586,9 +4837,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.7.42", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.42.tgz", - "integrity": "sha512-C5CYWaIZEyqPl5W/EwcJ/mLBJFHVoUEa/IwWi0b4q2fCXcSCktQGwKXOQ+d67GneiZoiq0HasgcdMmMpGS9YRQ==", + "version": "1.7.26", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.26.tgz", + "integrity": "sha512-9TNXPIJqFynlAOrRD6tUQjMq7KApSklK3R/tXgIxc7Qx+lWu8hlDQ/kVPLpU7PWvMMwC/3hKBW+p5f+Tms1hmA==", "cpu": [ "arm64" ], @@ -4602,9 +4853,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.7.42", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.42.tgz", - "integrity": "sha512-3j47seZ5pO62mbrqvPe1iwhe2BXnM5q7iB+n2xgA38PCGYt0mnaJafqmpCXm/uYZOCMqSNynaoOWCMMZm4sqtA==", + "version": "1.7.26", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.26.tgz", + "integrity": "sha512-9YngxNcG3177GYdsTum4V98Re+TlCeJEP4kEwEg9EagT5s3YejYdKwVAkAsJszzkXuyRDdnHUpYbTrPG6FiXrQ==", "cpu": [ "ia32" ], @@ -4618,9 +4869,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.7.42", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.42.tgz", - "integrity": "sha512-FXl9MdeUogZLGDcLr6QIRdDVkpG0dkN4MLM4dwQ5kcAk+XfKPrQibX6M2kcfhsCx+jtBqtK7hRFReRXPWJZGbA==", + "version": "1.7.26", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.26.tgz", + "integrity": "sha512-VR+hzg9XqucgLjXxA13MtV5O3C0bK0ywtLIBw/+a+O+Oc6mxFWHtdUeXDbIi5AiPbn0fjgVJMqYnyjGyyX8u0w==", "cpu": [ "x64" ], @@ -4640,18 +4891,18 @@ "license": "Apache-2.0" }, "node_modules/@swc/types": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.13.tgz", - "integrity": "sha512-JL7eeCk6zWCbiYQg2xQSdLXQJl8Qoc9rXmG2cEKvHe3CKwMHwHGpfOb8frzNLmbycOo6I51qxnLnn9ESf4I20Q==", + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.12.tgz", + "integrity": "sha512-wBJA+SdtkbFhHjTMYH+dEH1y4VpfGdAc2Kw/LK09i9bXd/K6j6PkDcFCEzb6iVfZMkPRrl/q0e3toqTAJdkIVA==", "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.3" } }, "node_modules/@tanstack/query-core": { - "version": "5.59.16", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.59.16.tgz", - "integrity": "sha512-crHn+G3ltqb5JG0oUv6q+PMz1m1YkjpASrXTU+sYWW9pLk0t2GybUHNRqYPZWhxgjPaVGC4yp92gSFEJgYEsPw==", + "version": "5.59.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.59.0.tgz", + "integrity": "sha512-WGD8uIhX6/deH/tkZqPNcRyAhDUqs729bWKoByYHSogcshXfFbppOdTER5+qY7mFvu8KEFJwT0nxr8RfPTVh0Q==", "license": "MIT", "funding": { "type": "github", @@ -4659,12 +4910,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.59.16", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.59.16.tgz", - "integrity": "sha512-MuyWheG47h6ERd4PKQ6V8gDyBu3ThNG22e1fRVwvq6ap3EqsFhyuxCAwhNP/03m/mLg+DAb0upgbPaX6VB+CkQ==", + "version": "5.59.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.59.0.tgz", + "integrity": "sha512-YDXp3OORbYR+8HNQx+lf4F73NoiCmCcSvZvgxE29OifmQFk0sBlO26NWLHpcNERo92tVk3w+JQ53/vkcRUY1hA==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.59.16" + "@tanstack/query-core": "5.59.0" }, "funding": { "type": "github", @@ -4712,11 +4963,10 @@ } }, "node_modules/@testing-library/jest-dom": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz", - "integrity": "sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==", + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.6.2.tgz", + "integrity": "sha512-P6GJD4yqc9jZLbe98j/EkyQDTPgqftohZF5FBkHY5BUERZmcf4HeO2k0XaefEg329ux2p21i1A1DmyQ1kKw2Jw==", "dev": true, - "license": "MIT", "dependencies": { "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", @@ -5051,9 +5301,9 @@ "license": "MIT" }, "node_modules/@types/d3-selection": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", - "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.10.tgz", + "integrity": "sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==", "license": "MIT" }, "node_modules/@types/d3-shape": { @@ -5084,9 +5334,9 @@ "license": "MIT" }, "node_modules/@types/d3-transition": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", - "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.8.tgz", + "integrity": "sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==", "license": "MIT", "dependencies": { "@types/d3-selection": "*" @@ -5142,9 +5392,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.14", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", - "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "version": "29.5.13", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.13.tgz", + "integrity": "sha512-wd+MVEZCHt23V0/L642O5APvspWply/rGY5BcW4SUETo2UzPU3Z26qr8jC2qxpimI2jjx9h7+2cj2FwIr01bXg==", "dev": true, "license": "MIT", "dependencies": { @@ -5199,15 +5449,14 @@ "integrity": "sha512-oonYDXI4GegGaG7FFVtriJ+Yqlh4YR3L3NVDiwCEBVG7sbya19SoGx4MW4kg1MCMRPgkbbFTck8YKJL8PrkDfA==", "deprecated": "This is a stub types definition. jspdf provides its own type definitions, so you do not need this installed.", "dev": true, - "license": "MIT", "dependencies": { "jspdf": "*" } }, "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.12", + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.12.tgz", + "integrity": "sha512-BK7XS+NyRI291HIo0HCfE18Lp8oA30H1gpi1tf0mF3TgiCEzanQjOqNZ4x126SXzzi2oNSZhZ5axJp1k0iM6jg==", "dev": true, "license": "MIT", "dependencies": { @@ -5215,9 +5464,9 @@ } }, "node_modules/@types/node": { - "version": "22.8.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.6.tgz", - "integrity": "sha512-tosuJYKrIqjQIlVCM4PEGxOmyg3FCPa/fViuJChnGeEIhjA46oy8FMVoF9su1/v8PNs2a8Q0iFNyOx0uOF91nw==", + "version": "22.8.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.4.tgz", + "integrity": "sha512-SpNNxkftTJOPk0oN+y2bIqurEXHTA2AOZ3EJDDKeJ5VzkvvORSvmQXGQarcOzWV1ac7DCaPBEdMDxBsM+d8jWw==", "license": "MIT", "dependencies": { "undici-types": "~6.19.8" @@ -5234,20 +5483,18 @@ "version": "6.9.16", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@types/raf": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", - "license": "MIT", "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.11", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.11.tgz", + "integrity": "sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==", "devOptional": true, "license": "MIT", "dependencies": { @@ -5256,9 +5503,9 @@ } }, "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.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", + "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", "dev": true, "license": "MIT", "dependencies": { @@ -5566,9 +5813,9 @@ "license": "ISC" }, "node_modules/@vitejs/plugin-react": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.3.tgz", - "integrity": "sha512-NooDe9GpHGqNns1i8XDERg0Vsg5SSYRhRxxyTGogUdkdNt47jal+fbuYi+Yfq6pzRCKXyoPcWisfxE6RIM3GKA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.2.tgz", + "integrity": "sha512-hieu+o05v4glEBucTcKMK3dlES0OeJlD9YVOAPraVMOInBCwzumaIFiUjr4bHK7NPgnAHgiskUoceKercrN8vg==", "license": "MIT", "dependencies": { "@babel/core": "^7.25.2", @@ -5597,21 +5844,21 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.4.tgz", - "integrity": "sha512-FPKQuJfR6VTfcNMcGpqInmtJuVXFSCd9HQltYncfR01AzXhLucMEtQ5SinPdZxsT5x/5BK7I5qFJ5/ApGCmyTQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.2.tgz", + "integrity": "sha512-b7kHrFrs2urS0cOk5N10lttI8UdJ/yP3nB4JYTREvR5o18cR99yPpK4gK8oQgI42BVv0ILWYUSYB7AXkAUDc0g==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.3.0", "@bcoe/v8-coverage": "^0.2.3", - "debug": "^4.3.7", + "debug": "^4.3.6", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-lib-source-maps": "^5.0.6", "istanbul-reports": "^3.1.7", - "magic-string": "^0.30.12", - "magicast": "^0.3.5", + "magic-string": "^0.30.11", + "magicast": "^0.3.4", "std-env": "^3.7.0", "test-exclude": "^7.0.1", "tinyrainbow": "^1.2.0" @@ -5620,8 +5867,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "2.1.4", - "vitest": "2.1.4" + "@vitest/browser": "2.1.2", + "vitest": "2.1.2" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -5630,15 +5877,15 @@ } }, "node_modules/@vitest/expect": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.4.tgz", - "integrity": "sha512-DOETT0Oh1avie/D/o2sgMHGrzYUFFo3zqESB2Hn70z6QB1HrS2IQ9z5DfyTqU8sg4Bpu13zZe9V4+UTNQlUeQA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.2.tgz", + "integrity": "sha512-FEgtlN8mIUSEAAnlvn7mP8vzaWhEaAEvhSXCqrsijM7K6QqjB11qoRZYEd4AKSCDz8p0/+yH5LzhZ47qt+EyPg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.4", - "@vitest/utils": "2.1.4", - "chai": "^5.1.2", + "@vitest/spy": "2.1.2", + "@vitest/utils": "2.1.2", + "chai": "^5.1.1", "tinyrainbow": "^1.2.0" }, "funding": { @@ -5646,21 +5893,22 @@ } }, "node_modules/@vitest/mocker": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.4.tgz", - "integrity": "sha512-Ky/O1Lc0QBbutJdW0rqLeFNbuLEyS+mIPiNdlVlp2/yhJ0SbyYqObS5IHdhferJud8MbbwMnexg4jordE5cCoQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.2.tgz", + "integrity": "sha512-ExElkCGMS13JAJy+812fw1aCv2QO/LBK6CyO4WOPAzLTmve50gydOlWhgdBJPx2ztbADUq3JVI0C5U+bShaeEA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.4", + "@vitest/spy": "^2.1.0-beta.1", "estree-walker": "^3.0.3", - "magic-string": "^0.30.12" + "magic-string": "^0.30.11" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "msw": "^2.4.9", + "@vitest/spy": "2.1.2", + "msw": "^2.3.5", "vite": "^5.0.0" }, "peerDependenciesMeta": { @@ -5683,9 +5931,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.4.tgz", - "integrity": "sha512-L95zIAkEuTDbUX1IsjRl+vyBSLh3PwLLgKpghl37aCK9Jvw0iP+wKwIFhfjdUtA2myLgjrG6VU6JCFLv8q/3Ww==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.2.tgz", + "integrity": "sha512-FIoglbHrSUlOJPDGIrh2bjX1sNars5HbxlcsFKCtKzu4+5lpsRhOCVcuzp0fEhAGHkPZRIXVNzPcpSlkoZ3LuA==", "dev": true, "license": "MIT", "dependencies": { @@ -5696,13 +5944,13 @@ } }, "node_modules/@vitest/runner": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.4.tgz", - "integrity": "sha512-sKRautINI9XICAMl2bjxQM8VfCMTB0EbsBc/EDFA57V6UQevEKY/TOPOF5nzcvCALltiLfXWbq4MaAwWx/YxIA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.2.tgz", + "integrity": "sha512-UCsPtvluHO3u7jdoONGjOSil+uON5SSvU9buQh3lP7GgUXHp78guN1wRmZDX4wGK6J10f9NUtP6pO+SFquoMlw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "2.1.4", + "@vitest/utils": "2.1.2", "pathe": "^1.1.2" }, "funding": { @@ -5710,14 +5958,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.4.tgz", - "integrity": "sha512-3Kab14fn/5QZRog5BPj6Rs8dc4B+mim27XaKWFWHWA87R56AKjHTGcBFKpvZKDzC4u5Wd0w/qKsUIio3KzWW4Q==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.2.tgz", + "integrity": "sha512-xtAeNsZ++aRIYIUsek7VHzry/9AcxeULlegBvsdLncLmNCR6tR8SRjn8BbDP4naxtccvzTqZ+L1ltZlRCfBZFA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.4", - "magic-string": "^0.30.12", + "@vitest/pretty-format": "2.1.2", + "magic-string": "^0.30.11", "pathe": "^1.1.2" }, "funding": { @@ -5725,27 +5973,27 @@ } }, "node_modules/@vitest/spy": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.4.tgz", - "integrity": "sha512-4JOxa+UAizJgpZfaCPKK2smq9d8mmjZVPMt2kOsg/R8QkoRzydHH1qHxIYNvr1zlEaFj4SXiaaJWxq/LPLKaLg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.2.tgz", + "integrity": "sha512-GSUi5zoy+abNRJwmFhBDC0yRuVUn8WMlQscvnbbXdKLXX9dE59YbfwXxuJ/mth6eeqIzofU8BB5XDo/Ns/qK2A==", "dev": true, "license": "MIT", "dependencies": { - "tinyspy": "^3.0.2" + "tinyspy": "^3.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.4.tgz", - "integrity": "sha512-MXDnZn0Awl2S86PSNIim5PWXgIAx8CIkzu35mBdSApUip6RFOGXBCf3YFyeEu8n1IHk4bWD46DeYFu9mQlFIRg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.2.tgz", + "integrity": "sha512-zMO2KdYy6mx56btx9JvAqAZ6EyS3g49krMPPrgOp1yxGZiA93HumGk+bZ5jIZtOg5/VBYl5eBmGRQHqq4FG6uQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.4", - "loupe": "^3.1.2", + "@vitest/pretty-format": "2.1.2", + "loupe": "^3.1.1", "tinyrainbow": "^1.2.0" }, "funding": { @@ -5753,9 +6001,9 @@ } }, "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "dev": true, "license": "MIT", "bin": { @@ -5805,6 +6053,19 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/amazon-cognito-identity-js": { + "version": "6.3.13", + "resolved": "https://registry.npmjs.org/amazon-cognito-identity-js/-/amazon-cognito-identity-js-6.3.13.tgz", + "integrity": "sha512-AOROAQHQYvXYnhzhB9L1cZdz+linq/xaPTBfXhvXsx1tyhbbzmA7HX8Ap3mKBPsjsG8UWfzDhdRCb7hmH3S14A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "1.2.2", + "buffer": "4.9.2", + "fast-base64-decode": "^1.0.0", + "isomorphic-unfetch": "^3.0.0", + "js-cookie": "^2.2.1" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -6052,7 +6313,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "license": "(MIT OR Apache-2.0)", "bin": { "atob": "bin/atob.js" }, @@ -6077,25 +6337,25 @@ } }, "node_modules/aws-amplify": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/aws-amplify/-/aws-amplify-6.8.0.tgz", - "integrity": "sha512-/j88ZlyQWnz8UaHBRFpciFBZUmtrd92BfwJMeSPVbBySyVv4vZC8wRCiUkkXSVy0QbVv6nvpMnPnf6fexYnLGw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-amplify/analytics": "7.0.56", - "@aws-amplify/api": "6.1.1", - "@aws-amplify/auth": "6.6.0", - "@aws-amplify/core": "6.5.1", - "@aws-amplify/datastore": "5.0.58", - "@aws-amplify/notifications": "2.0.56", - "@aws-amplify/storage": "6.6.14", + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/aws-amplify/-/aws-amplify-6.6.3.tgz", + "integrity": "sha512-gGpr3o3GggZpw3uZKwdWFfemju3yz6lKjLe68zNe1HMAgxoW790aW2apxkS2RgadREh8TBF5R5Hx6aRn1btd2Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/analytics": "7.0.50", + "@aws-amplify/api": "6.0.52", + "@aws-amplify/auth": "6.5.0", + "@aws-amplify/core": "6.4.3", + "@aws-amplify/datastore": "5.0.52", + "@aws-amplify/notifications": "2.0.50", + "@aws-amplify/storage": "6.6.8", "tslib": "^2.5.0" } }, "node_modules/axe-core": { - "version": "4.10.2", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.2.tgz", - "integrity": "sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.0.tgz", + "integrity": "sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==", "dev": true, "license": "MPL-2.0", "engines": { @@ -6134,7 +6394,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", - "license": "MIT", "optional": true, "engines": { "node": ">= 0.6.0" @@ -6180,6 +6439,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -6189,9 +6449,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", - "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", + "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", "funding": [ { "type": "opencollective", @@ -6208,10 +6468,10 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001669", - "electron-to-chromium": "^1.5.41", + "caniuse-lite": "^1.0.30001663", + "electron-to-chromium": "^1.5.28", "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.1" + "update-browserslist-db": "^1.1.0" }, "bin": { "browserslist": "cli.js" @@ -6224,7 +6484,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", - "license": "(MIT OR Apache-2.0)", "bin": { "btoa": "bin/btoa.js" }, @@ -6317,9 +6576,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001676", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001676.tgz", - "integrity": "sha512-Qz6zwGCiPghQXGJvgQAem79esjitvJ+CxSbSQkW9H/UX5hg8XM88d4lp2W+MEQ81j+Hip58Il+jGVdazk1z9cw==", + "version": "1.0.30001666", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001666.tgz", + "integrity": "sha512-gD14ICmoV5ZZM1OdzPWmpx+q4GyefaK06zi8hmfHV5xe4/2nOQX3+Dw5o+fSqOws2xVwL9j+anOPFwHzdEdV4g==", "funding": [ { "type": "opencollective", @@ -6340,7 +6599,6 @@ "version": "3.0.10", "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.10.tgz", "integrity": "sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q==", - "license": "MIT", "optional": true, "dependencies": { "@babel/runtime": "^7.12.5", @@ -6360,7 +6618,6 @@ "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "license": "MIT", "optional": true }, "node_modules/carbon-components": { @@ -6384,9 +6641,9 @@ "license": "MIT" }, "node_modules/chai": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", - "integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz", + "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==", "dev": true, "license": "MIT", "dependencies": { @@ -6565,11 +6822,10 @@ } }, "node_modules/core-js": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", - "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==", + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz", + "integrity": "sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==", "hasInstallScript": true, - "license": "MIT", "optional": true, "funding": { "type": "opencollective", @@ -6621,7 +6877,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", - "license": "MIT", "optional": true, "dependencies": { "utrie": "^1.0.2" @@ -7236,6 +7491,46 @@ "node": ">=6" } }, + "node_modules/deep-equal": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.5", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.2", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deep-equal/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -7306,18 +7601,6 @@ "node": ">=6" } }, - "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "license": "Apache-2.0", - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/diff-sequences": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", @@ -7408,9 +7691,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.50", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.50.tgz", - "integrity": "sha512-eMVObiUQ2LdgeO1F/ySTXsvqvxb6ZH2zPGaMYsWzRDdOddUa77tdmI0ltg+L16UpbWdhPmuF3wIQYyQq65WfZw==", + "version": "1.5.31", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.31.tgz", + "integrity": "sha512-QcDoBbQeYt0+3CWcK/rEbuHvwpbT/8SV9T3OSgs6cX1FlcUAkgrkqbg9zLnDrMM/rLamzQwal4LYFCiWk861Tg==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -7529,10 +7812,38 @@ "node": ">= 0.4" } }, + "node_modules/es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-get-iterator/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, "node_modules/es-iterator-helpers": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.1.0.tgz", - "integrity": "sha512-/SurEfycdyssORP/E+bj4sEu1CWw4EmLDsHynHwSXQ7utgbrMRWW195pTrCjFgFCddf/UkYm3oqKPRq5i8bJbw==", + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz", + "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==", "dev": true, "license": "MIT", "dependencies": { @@ -7543,12 +7854,12 @@ "es-set-tostringtag": "^2.0.3", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.4", + "globalthis": "^1.0.3", "has-property-descriptors": "^1.0.2", "has-proto": "^1.0.3", "has-symbols": "^1.0.3", "internal-slot": "^1.0.7", - "iterator.prototype": "^1.1.3", + "iterator.prototype": "^1.1.2", "safe-array-concat": "^1.1.2" }, "engines": { @@ -7682,7 +7993,6 @@ "version": "8.57.1", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", - "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", "dependencies": { @@ -7873,9 +8183,9 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.31.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", - "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.30.0.tgz", + "integrity": "sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw==", "dev": true, "license": "MIT", "dependencies": { @@ -7887,7 +8197,7 @@ "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.12.0", + "eslint-module-utils": "^2.9.0", "hasown": "^2.0.2", "is-core-module": "^2.15.1", "is-glob": "^4.0.3", @@ -7896,14 +8206,13 @@ "object.groupby": "^1.0.3", "object.values": "^1.2.0", "semver": "^6.3.1", - "string.prototype.trimend": "^1.0.8", "tsconfig-paths": "^3.15.0" }, "engines": { "node": ">=4" }, "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" } }, "node_modules/eslint-plugin-import/node_modules/brace-expansion": { @@ -7990,9 +8299,9 @@ } }, "node_modules/eslint-plugin-jsdoc/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", + "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", "dev": true, "license": "Apache-2.0", "engines": { @@ -8003,15 +8312,15 @@ } }, "node_modules/eslint-plugin-jsdoc/node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.2.0.tgz", + "integrity": "sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.14.0", + "acorn": "^8.12.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" + "eslint-visitor-keys": "^4.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -8021,13 +8330,13 @@ } }, "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", - "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.0.tgz", + "integrity": "sha512-ySOHvXX8eSN6zz8Bywacm7CvGNhUtdjvqfQDVe6020TUK34Cywkw7m0KsCCk1Qtm9G1FayfTN1/7mMYnYO2Bhg==", "dev": true, "license": "MIT", "dependencies": { - "aria-query": "^5.3.2", + "aria-query": "~5.1.3", "array-includes": "^3.1.8", "array.prototype.flatmap": "^1.3.2", "ast-types-flow": "^0.0.8", @@ -8035,13 +8344,14 @@ "axobject-query": "^4.1.0", "damerau-levenshtein": "^1.0.8", "emoji-regex": "^9.2.2", + "es-iterator-helpers": "^1.0.19", "hasown": "^2.0.2", "jsx-ast-utils": "^3.3.5", "language-tags": "^1.0.9", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "safe-regex-test": "^1.0.3", - "string.prototype.includes": "^2.0.1" + "string.prototype.includes": "^2.0.0" }, "engines": { "node": ">=4.0" @@ -8051,13 +8361,13 @@ } }, "node_modules/eslint-plugin-jsx-a11y/node_modules/aria-query": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", - "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", "dev": true, "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" + "dependencies": { + "deep-equal": "^2.0.5" } }, "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": { @@ -8170,9 +8480,9 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.37.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz", - "integrity": "sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w==", + "version": "7.37.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.1.tgz", + "integrity": "sha512-xwTnwDqzbDRA8uJ7BMxPs/EXRB3i8ZfnOIp8BsxEQkT0nHPp+WWceqGgo6rKb9ctNi8GJLDT4Go5HAWELa/WMg==", "dev": true, "license": "MIT", "dependencies": { @@ -8181,7 +8491,7 @@ "array.prototype.flatmap": "^1.3.2", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.1.0", + "es-iterator-helpers": "^1.0.19", "estraverse": "^5.3.0", "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", @@ -8395,9 +8705,9 @@ } }, "node_modules/esri-leaflet": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/esri-leaflet/-/esri-leaflet-3.0.13.tgz", - "integrity": "sha512-QP831w3vv2Hfy8aWUcUHJShSrg+EeIt5vxtTJZEHbgLzjS89QidEo1GrZ51u5KAIMuq8WPmNAurstU2AaCPS8g==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/esri-leaflet/-/esri-leaflet-3.0.12.tgz", + "integrity": "sha512-Yi7oH/mK4quOlCe920yuEYvUk0BjJRjmmE78ReAdJT5EbibW5wJoT9DtvG3JEJD22mQ0oF1LhcfL0Wb5jRhDAQ==", "license": "Apache-2.0", "peer": true, "dependencies": { @@ -8502,15 +8812,11 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/expect-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", - "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.0.0" - } + "node_modules/fast-base64-decode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz", + "integrity": "sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q==", + "license": "MIT" }, "node_modules/fast-deep-equal": { "version": "3.1.3", @@ -8598,8 +8904,7 @@ "node_modules/fflate": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", - "license": "MIT" + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" }, "node_modules/file-entry-cache": { "version": "6.0.1", @@ -8618,6 +8923,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -8719,9 +9025,9 @@ } }, "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -8807,6 +9113,16 @@ "license": "ISC", "peer": true }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -9145,7 +9461,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", - "license": "MIT", "optional": true, "dependencies": { "css-line-break": "^2.1.0", @@ -9335,6 +9650,23 @@ "loose-envify": "^1.0.0" } }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-array-buffer": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", @@ -9485,6 +9817,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -9533,6 +9866,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -9571,6 +9905,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -9759,6 +10094,16 @@ "dev": true, "license": "ISC" }, + "node_modules/isomorphic-unfetch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz", + "integrity": "sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.1", + "unfetch": "^4.2.0" + } + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -9814,9 +10159,9 @@ } }, "node_modules/iterator.prototype": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.3.tgz", - "integrity": "sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", + "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", "dev": true, "license": "MIT", "dependencies": { @@ -9825,9 +10170,6 @@ "has-symbols": "^1.0.3", "reflect.getprototypeof": "^1.0.4", "set-function-name": "^2.0.1" - }, - "engines": { - "node": ">= 0.4" } }, "node_modules/jackspeak": { @@ -10012,13 +10354,10 @@ } }, "node_modules/js-cookie": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", - "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", - "license": "MIT", - "engines": { - "node": ">=14" - } + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", + "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==", + "license": "MIT" }, "node_modules/js-tokens": { "version": "4.0.0", @@ -10144,7 +10483,6 @@ "version": "2.5.2", "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-2.5.2.tgz", "integrity": "sha512-myeX9c+p7znDWPk0eTrujCzNjT+CXdXyk7YmJq5nD5V7uLLKmSXnlQ/Jn/kuo3X09Op70Apm0rQSnFWyGK8uEQ==", - "license": "MIT", "dependencies": { "@babel/runtime": "^7.23.2", "atob": "^2.1.2", @@ -10159,19 +10497,17 @@ } }, "node_modules/jspdf-autotable": { - "version": "3.8.4", - "resolved": "https://registry.npmjs.org/jspdf-autotable/-/jspdf-autotable-3.8.4.tgz", - "integrity": "sha512-rSffGoBsJYX83iTRv8Ft7FhqfgEL2nLpGAIiqruEQQ3e4r0qdLFbPUB7N9HAle0I3XgpisvyW751VHCqKUVOgQ==", - "license": "MIT", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/jspdf-autotable/-/jspdf-autotable-3.8.3.tgz", + "integrity": "sha512-PQFdljBt+ijm6ZWXYxhZ54A/awV63UKcipYoA2+YGsz0BXXiXTIL/FIg+V30j7wPdSdzClfbB3qKX9UeuFylPQ==", "peerDependencies": { "jspdf": "^2.5.1" } }, "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==", - "license": "(MPL-2.0 OR Apache-2.0)", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.6.tgz", + "integrity": "sha512-zUTaUBO8pY4+iJMPE1B9XlO2tXVYIcEA4SNGtvDELzTSCQO7RzH+j7S180BmhmJId78lqGU2z19vgVx2Sxs/PQ==", "optional": true }, "node_modules/jsx-ast-utils": { @@ -10230,8 +10566,7 @@ "node_modules/leaflet": { "version": "1.9.4", "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", - "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==", - "license": "BSD-2-Clause" + "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==" }, "node_modules/leaflet.heat": { "version": "0.2.0", @@ -10360,11 +10695,14 @@ "license": "MIT" }, "node_modules/loupe": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz", - "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.1.tgz", + "integrity": "sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.1" + } }, "node_modules/lower-case": { "version": "2.0.2", @@ -10395,9 +10733,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.12", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", - "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", + "version": "0.30.11", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", "dev": true, "license": "MIT", "dependencies": { @@ -10481,6 +10819,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -10611,12 +10950,48 @@ "tslib": "^2.0.3" } }, - "node_modules/node-addon-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "license": "MIT" }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-releases": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", @@ -10651,6 +11026,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -10859,13 +11251,13 @@ } }, "node_modules/parse5": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", - "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", "dev": true, "license": "MIT", "dependencies": { - "entities": "^4.5.0" + "entities": "^4.4.0" }, "funding": { "url": "https://github.com/inikulin/parse5?sponsor=1" @@ -10976,13 +11368,12 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "license": "MIT", "optional": true }, "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", "license": "ISC" }, "node_modules/picomatch": { @@ -11131,7 +11522,6 @@ "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.0.6" }, @@ -11174,7 +11564,6 @@ "version": "3.4.1", "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", - "license": "MIT", "optional": true, "dependencies": { "performance-now": "^2.1.0" @@ -11299,12 +11688,12 @@ } }, "node_modules/react-router": { - "version": "6.27.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.27.0.tgz", - "integrity": "sha512-YA+HGZXz4jaAkVoYBE98VQl+nVzI+cVI2Oj/06F5ZM+0u3TgedN9Y9kmMRo2mnkSK2nCpNQn0DVob4HCsY/WLw==", + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.2.tgz", + "integrity": "sha512-tvN1iuT03kHgOFnLPfLJ8V95eijteveqdOSk+srqfePtQvqCExB8eHOYnlilbOcyJyKnYkr1vJvf7YqotAJu1A==", "license": "MIT", "dependencies": { - "@remix-run/router": "1.20.0" + "@remix-run/router": "1.19.2" }, "engines": { "node": ">=14.0.0" @@ -11314,13 +11703,13 @@ } }, "node_modules/react-router-dom": { - "version": "6.27.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.27.0.tgz", - "integrity": "sha512-+bvtFWMC0DgAFrfKXKG9Fc+BcXWRUO1aJIihbB79xaeq0v5UzfvnM5houGUm1Y461WVRcgAQ+Clh5rdb1eCx4g==", + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.2.tgz", + "integrity": "sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==", "license": "MIT", "dependencies": { - "@remix-run/router": "1.20.0", - "react-router": "6.27.0" + "@remix-run/router": "1.19.2", + "react-router": "6.26.2" }, "engines": { "node": ">=14.0.0" @@ -11331,9 +11720,9 @@ } }, "node_modules/readdirp": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", - "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.1.tgz", + "integrity": "sha512-GkMg9uOTpIWWKbSsgwb5fA4EavTR+SG/PMPoAY8hkhHfEEY0/vqljY+XHqtDf2cr2IJtoNRDbrrEpZUiZCkYRw==", "license": "MIT", "engines": { "node": ">= 14.16.0" @@ -11401,16 +11790,16 @@ "license": "MIT" }, "node_modules/regexp.prototype.flags": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", - "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.6", "define-properties": "^1.2.1", "es-errors": "^1.3.0", - "set-function-name": "^2.0.2" + "set-function-name": "^2.0.1" }, "engines": { "node": ">= 0.4" @@ -11487,7 +11876,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", - "license": "MIT OR SEE LICENSE IN FEEL-FREE.md", "optional": true, "engines": { "node": ">= 0.8.15" @@ -11517,9 +11905,9 @@ "license": "Unlicense" }, "node_modules/rollup": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.3.tgz", - "integrity": "sha512-HBW896xR5HGmoksbi3JBDtmVzWiPAYqp7wip50hjQ67JbDz61nyoMPdqu1DvVW9asYb2M65Z20ZHsyJCMqMyDg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", + "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", "license": "MIT", "dependencies": { "@types/estree": "1.0.6" @@ -11532,24 +11920,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.24.3", - "@rollup/rollup-android-arm64": "4.24.3", - "@rollup/rollup-darwin-arm64": "4.24.3", - "@rollup/rollup-darwin-x64": "4.24.3", - "@rollup/rollup-freebsd-arm64": "4.24.3", - "@rollup/rollup-freebsd-x64": "4.24.3", - "@rollup/rollup-linux-arm-gnueabihf": "4.24.3", - "@rollup/rollup-linux-arm-musleabihf": "4.24.3", - "@rollup/rollup-linux-arm64-gnu": "4.24.3", - "@rollup/rollup-linux-arm64-musl": "4.24.3", - "@rollup/rollup-linux-powerpc64le-gnu": "4.24.3", - "@rollup/rollup-linux-riscv64-gnu": "4.24.3", - "@rollup/rollup-linux-s390x-gnu": "4.24.3", - "@rollup/rollup-linux-x64-gnu": "4.24.3", - "@rollup/rollup-linux-x64-musl": "4.24.3", - "@rollup/rollup-win32-arm64-msvc": "4.24.3", - "@rollup/rollup-win32-ia32-msvc": "4.24.3", - "@rollup/rollup-win32-x64-msvc": "4.24.3", + "@rollup/rollup-android-arm-eabi": "4.24.0", + "@rollup/rollup-android-arm64": "4.24.0", + "@rollup/rollup-darwin-arm64": "4.24.0", + "@rollup/rollup-darwin-x64": "4.24.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", + "@rollup/rollup-linux-arm-musleabihf": "4.24.0", + "@rollup/rollup-linux-arm64-gnu": "4.24.0", + "@rollup/rollup-linux-arm64-musl": "4.24.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", + "@rollup/rollup-linux-riscv64-gnu": "4.24.0", + "@rollup/rollup-linux-s390x-gnu": "4.24.0", + "@rollup/rollup-linux-x64-gnu": "4.24.0", + "@rollup/rollup-linux-x64-musl": "4.24.0", + "@rollup/rollup-win32-arm64-msvc": "4.24.0", + "@rollup/rollup-win32-ia32-msvc": "4.24.0", + "@rollup/rollup-win32-x64-msvc": "4.24.0", "fsevents": "~2.3.2" } }, @@ -11650,12 +12036,11 @@ "license": "MIT" }, "node_modules/sass": { - "version": "1.80.5", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.5.tgz", - "integrity": "sha512-TQd2aoQl/+zsxRMEDSxVdpPIqeq9UFc6pr7PzkugiTx3VYCFPUaa3P4RrBQsqok4PO200Vkz0vXQBNlg7W907g==", + "version": "1.79.4", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.79.4.tgz", + "integrity": "sha512-K0QDSNPXgyqO4GZq2HO5Q70TLxTH6cIT59RdoCHMivrC8rqzaTw5ab9prjz9KUN1El4FLXrBXJhik61JR4HcGg==", "license": "MIT", "dependencies": { - "@parcel/watcher": "^2.4.1", "chokidar": "^4.0.0", "immutable": "^4.0.0", "source-map-js": ">=0.6.2 <2.0.0" @@ -11668,9 +12053,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.2", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.2.tgz", + "integrity": "sha512-Ll6iXZ1EYwYT19SqW4mSBb76vSSi8JgzElmzIerhEGgzB5hRjDQIWsPmuk1UrAXkR16KJHqVY0eH+5/uw9Tmfw==", "dev": true, "license": "MIT", "dependencies": { @@ -11941,7 +12326,6 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz", "integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==", - "license": "MIT", "optional": true, "engines": { "node": ">=0.1.14" @@ -11954,6 +12338,19 @@ "dev": true, "license": "MIT" }, + "node_modules/stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "internal-slot": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -12025,18 +12422,14 @@ } }, "node_modules/string.prototype.includes": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", - "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.0.tgz", + "integrity": "sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3" - }, - "engines": { - "node": ">= 0.4" + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" } }, "node_modules/string.prototype.matchall": { @@ -12244,7 +12637,6 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", - "license": "MIT", "optional": true, "engines": { "node": ">=12.0.0" @@ -12258,9 +12650,9 @@ "license": "MIT" }, "node_modules/synckit": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", - "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz", + "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==", "dev": true, "license": "MIT", "dependencies": { @@ -12336,7 +12728,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", - "license": "MIT", "optional": true, "dependencies": { "utrie": "^1.0.2" @@ -12364,9 +12755,9 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.1.tgz", - "integrity": "sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.0.tgz", + "integrity": "sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==", "dev": true, "license": "MIT" }, @@ -12408,29 +12799,39 @@ } }, "node_modules/tldts": { - "version": "6.1.57", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.57.tgz", - "integrity": "sha512-Oy7yDXK8meJl8vPMOldzA+MtueAJ5BrH4l4HXwZuj2AtfoQbLjmTJmjNWPUcAo+E/ibHn7QlqMS0BOcXJFJyHQ==", + "version": "6.1.49", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.49.tgz", + "integrity": "sha512-E5se9HuCyfwWvmc0JiXiocOw+Cm4tlJCKewdB5RKMH8MmtiTsQCc+yu5BBYB5ZN4lNbz8Xg65bqJ1odS9+RhIA==", "dev": true, "license": "MIT", "dependencies": { - "tldts-core": "^6.1.57" + "tldts-core": "^6.1.49" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "6.1.57", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.57.tgz", - "integrity": "sha512-lXnRhuQpx3zU9EONF9F7HfcRLvN1uRYUBIiKL+C/gehC/77XTU+Jye6ui86GA3rU6FjlJ0triD1Tkjt2F/2lEg==", + "version": "6.1.49", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.49.tgz", + "integrity": "sha512-ctRO/wzBasOCxAStJG/60Qe8/QpGmaVPsE8djdk0vioxN4uCOgKoveH71Qc2EOmVMIjVf0BjigI5p9ZDuLOygg==", "dev": true, "license": "MIT" }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -12492,9 +12893,9 @@ } }, "node_modules/ts-api-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz", - "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, "license": "MIT", "engines": { @@ -12505,9 +12906,9 @@ } }, "node_modules/tsconfck": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.4.tgz", - "integrity": "sha512-kdqWFGVJqe+KGYvlSO9NIaWn9jT1Ny4oKVzAJsKii5eoE9snzTJzL4+MMVOMn+fikWGFmKEylcXL710V/kIPJQ==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.3.tgz", + "integrity": "sha512-ulNZP1SVpRDesxeMLON/LtWM8HIgAJEIVpVVhBM6gsmvQ8+Rh+ZG7FWGvHh7Ah3pRABwVJWklWCr/BTZSv0xnQ==", "license": "MIT", "bin": { "tsconfck": "bin/tsconfck.js" @@ -12551,9 +12952,9 @@ } }, "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", "license": "0BSD" }, "node_modules/type-check": { @@ -12660,9 +13061,9 @@ } }, "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", "devOptional": true, "license": "Apache-2.0", "bin": { @@ -12704,6 +13105,12 @@ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "license": "MIT" }, + "node_modules/unfetch": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz", + "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==", + "license": "MIT" + }, "node_modules/update-browserslist-db": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", @@ -12770,7 +13177,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", - "license": "MIT", "optional": true, "dependencies": { "base64-arraybuffer": "^1.0.2" @@ -12790,9 +13196,9 @@ } }, "node_modules/vite": { - "version": "5.4.10", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.10.tgz", - "integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==", + "version": "5.4.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", + "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==", "license": "MIT", "dependencies": { "esbuild": "^0.21.3", @@ -12849,14 +13255,14 @@ } }, "node_modules/vite-node": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.4.tgz", - "integrity": "sha512-kqa9v+oi4HwkG6g8ufRnb5AeplcRw8jUF6/7/Qz1qRQOXHImG8YnLbB+LLszENwFnoBl9xIf9nVdCFzNd7GQEg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.2.tgz", + "integrity": "sha512-HPcGNN5g/7I2OtPjLqgOtCRu/qhVvBxTUD3qzitmL0SrG1cWFzxzhMDWussxSbrRYWqnKf8P2jiNhPMSN+ymsQ==", "dev": true, "license": "MIT", "dependencies": { "cac": "^6.7.14", - "debug": "^4.3.7", + "debug": "^4.3.6", "pathe": "^1.1.2", "vite": "^5.0.0" }, @@ -12904,31 +13310,30 @@ } }, "node_modules/vitest": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.4.tgz", - "integrity": "sha512-eDjxbVAJw1UJJCHr5xr/xM86Zx+YxIEXGAR+bmnEID7z9qWfoxpHw0zdobz+TQAFOLT+nEXz3+gx6nUJ7RgmlQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/expect": "2.1.4", - "@vitest/mocker": "2.1.4", - "@vitest/pretty-format": "^2.1.4", - "@vitest/runner": "2.1.4", - "@vitest/snapshot": "2.1.4", - "@vitest/spy": "2.1.4", - "@vitest/utils": "2.1.4", - "chai": "^5.1.2", - "debug": "^4.3.7", - "expect-type": "^1.1.0", - "magic-string": "^0.30.12", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.2.tgz", + "integrity": "sha512-veNjLizOMkRrJ6xxb+pvxN6/QAWg95mzcRjtmkepXdN87FNfxAss9RKe2far/G9cQpipfgP2taqg0KiWsquj8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "2.1.2", + "@vitest/mocker": "2.1.2", + "@vitest/pretty-format": "^2.1.2", + "@vitest/runner": "2.1.2", + "@vitest/snapshot": "2.1.2", + "@vitest/spy": "2.1.2", + "@vitest/utils": "2.1.2", + "chai": "^5.1.1", + "debug": "^4.3.6", + "magic-string": "^0.30.11", "pathe": "^1.1.2", "std-env": "^3.7.0", "tinybench": "^2.9.0", - "tinyexec": "^0.3.1", - "tinypool": "^1.0.1", + "tinyexec": "^0.3.0", + "tinypool": "^1.0.0", "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "2.1.4", + "vite-node": "2.1.2", "why-is-node-running": "^2.3.0" }, "bin": { @@ -12943,8 +13348,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.1.4", - "@vitest/ui": "2.1.4", + "@vitest/browser": "2.1.2", + "@vitest/ui": "2.1.2", "happy-dom": "*", "jsdom": "*" }, @@ -13004,10 +13409,9 @@ } }, "node_modules/web-vitals": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", - "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==", - "license": "Apache-2.0" + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.3.tgz", + "integrity": "sha512-/CFAm1mNxSmOj6i0Co+iGFJ58OS4NRGVP+AWS/l509uIK5a1bSoIVaHz/ZumpHTfHSZBpgrJ+wjfpAOrTHok5Q==" }, "node_modules/webidl-conversions": { "version": "7.0.0", @@ -13349,9 +13753,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", - "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", + "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", "license": "ISC", "bin": { "yaml": "bin.mjs" diff --git a/frontend/package.json b/frontend/package.json index 355b73bc..7772c0a6 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,11 +9,10 @@ "@carbon/icons-react": "^11.50.1", "@carbon/pictograms-react": "^11.49.0", "@carbon/react": "^1.27.0", - "@redux-devtools/extension": "^3.3.0", "@tanstack/react-query": "^5.50.1", "@types/node": "^22.0.0", "@vitejs/plugin-react": "^4.0.4", - "@vitejs/plugin-react-swc": "^3.3.2", + "@vitejs/plugin-react-swc": "^3.3.2", "aws-amplify": "^6.7.0", "axios": "^1.6.8", "jspdf": "^2.5.2", @@ -26,10 +25,7 @@ "react-esri-leaflet": "^2.0.1", "react-hash-string": "^1.0.0", "react-leaflet": "^4.2.1", - "react-redux": "^9.0.0", "react-router-dom": "^6.10.0", - "redux": "^5.0.0", - "redux-thunk": "^3.0.0", "vite": "^5.0.0", "vite-plugin-svgr": "^4.0.0", "vite-tsconfig-paths": "^5.0.0", @@ -59,7 +55,7 @@ }, "devDependencies": { "@testing-library/dom": "^10.2.0", - "@testing-library/jest-dom": "^6.4.5", + "@testing-library/jest-dom": "^6.6.2", "@testing-library/react": "^16.0.0", "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.5.12", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 79bc4953..1895b049 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -2,7 +2,6 @@ import { createBrowserRouter, RouterProvider } from 'react-router-dom'; import './custom.scss'; import Landing from "./screens/Landing"; import Help from "./screens/Help"; -import Reports from './screens/Reports'; import SideLayout from './layouts/SideLayout'; import ProtectedRoute from './routes/ProtectedRoute'; import Opening from './screens/Opening'; @@ -35,10 +34,6 @@ const router = createBrowserRouter([ path: "/silviculture-search", element: } /> }, - { - path: "/opening/reports", - element: } /> - }, { path: "/help", element: } /> diff --git a/frontend/src/__test__/actions/userAction.test.ts b/frontend/src/__test__/actions/userAction.test.ts deleted file mode 100644 index 592f409f..00000000 --- a/frontend/src/__test__/actions/userAction.test.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { describe, it, expect, vi } from 'vitest'; -import { getUserDetails, setClientRoles } from '../../actions/userAction'; -import { - USER_DETAILS_REQUEST, -USER_DETAILS_SUCCESS, -USER_DETAILS_FAIL, -SET_CLIENT_ROLES -} from '../../constants/userConstants'; -import { useGetAuth } from '../../contexts/AuthProvider'; -import { AppDispatch } from '../../store'; -import { UserClientRolesType } from '../../types/UserRoleType'; - - - -vi.mock('../../contexts/AuthProvider', () => ({ -useGetAuth: vi.fn(), -})); - -describe('userAction', () => { -describe('getUserDetails', () => { - it('should dispatch USER_DETAILS_REQUEST and USER_DETAILS_SUCCESS with user data when successful', async () => { - const mockDispatch = vi.fn(); - const mockIsLoggedIn = true; - const mockUser = { firstName: 'John', lastName: 'Doe' }; - const mockUserJSON = JSON.stringify(mockUser); - - (useGetAuth as vi.Mock).mockReturnValue({ isLoggedIn: mockIsLoggedIn }); - localStorage.setItem('famLoginUser', mockUserJSON); - - await getUserDetails()(mockDispatch as unknown as AppDispatch); - - expect(mockDispatch).toHaveBeenCalledWith({ type: USER_DETAILS_REQUEST }); - expect(mockDispatch).toHaveBeenCalledWith({ - type: USER_DETAILS_SUCCESS, - payload: { ...mockUser, isLoggedIn: mockIsLoggedIn }, - }); - }); - - it('should dispatch USER_DETAILS_FAIL with error when an error occurs', async () => { - const mockDispatch = vi.fn(); - const mockError = new Error('Test error'); - - (useGetAuth as vi.Mock).mockImplementation(() => { - throw mockError; - }); - - await getUserDetails()(mockDispatch as unknown as AppDispatch); - - expect(mockDispatch).toHaveBeenCalledWith({ type: USER_DETAILS_REQUEST }); - expect(mockDispatch).toHaveBeenCalledWith({ - type: USER_DETAILS_FAIL, - payload: { error: mockError }, - }); - }); - - it('should handle missing user data in localStorage', async () => { - const mockDispatch = vi.fn(); - const mockIsLoggedIn = true; - - (useGetAuth as vi.Mock).mockReturnValue({ isLoggedIn: mockIsLoggedIn }); - localStorage.removeItem('famLoginUser'); - - await getUserDetails()(mockDispatch as unknown as AppDispatch); - - expect(mockDispatch).toHaveBeenCalledWith({ type: USER_DETAILS_REQUEST }); - expect(mockDispatch).toHaveBeenCalledWith({ - type: USER_DETAILS_SUCCESS, - payload: { isLoggedIn: mockIsLoggedIn }, - }); - }); -}); - -describe('setClientRoles', () => { - it('should dispatch SET_CLIENT_ROLES with client roles', () => { - const mockDispatch = vi.fn(); - const mockClientRoles: UserClientRolesType[] = [{ clientId: '123', roles: ['admin'] }]; - - setClientRoles(mockClientRoles)(mockDispatch as unknown as AppDispatch); - - expect(mockDispatch).toHaveBeenCalledWith({ - type: SET_CLIENT_ROLES, - payload: mockClientRoles, - }); - }); -}); -}); \ No newline at end of file diff --git a/frontend/src/__test__/components/BCHeaderwSide.test.tsx b/frontend/src/__test__/components/BCHeaderwSide.test.tsx index c3a49c78..5a920686 100644 --- a/frontend/src/__test__/components/BCHeaderwSide.test.tsx +++ b/frontend/src/__test__/components/BCHeaderwSide.test.tsx @@ -5,9 +5,6 @@ import { BrowserRouter } from 'react-router-dom'; import BCHeaderwSide from '../../components/BCHeaderwSide'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import {leftMenu } from '../../components/BCHeaderwSide/constants'; -import * as redux from 'react-redux'; -import { Provider } from 'react-redux'; -import store from '../../store'; import { UserClientRolesType } from '../../types/UserRoleType'; import '@testing-library/jest-dom'; import { AuthProvider } from '../../contexts/AuthProvider'; @@ -31,12 +28,10 @@ const renderComponent = () => { render( - - - - - - + + + + ); @@ -65,9 +60,6 @@ const state = { }, }; -vi.spyOn(redux, 'useSelector') - .mockImplementation((callback) => callback(state)); - describe('BCHeaderwSide', () => { it('should renders the component', () => { renderComponent(); @@ -88,12 +80,6 @@ describe('BCHeaderwSide', () => { // expect(screen.queryByText('My Profile')).not.toBeVisible(); }); - // it('renders the correct number of top-level menu items', () => { - // renderComponent(); - // const menuItems = screen.getAllByRole('button', { name: /.*Category.*/ }); - // expect(menuItems).toHaveLength(leftMenu.length); - // }); - it('renders the correct menu item names', () => { renderComponent(); leftMenu.forEach(item => { diff --git a/frontend/src/__test__/components/BarChartGrouped.test.tsx b/frontend/src/__test__/components/BarChartGrouped.test.tsx index 16255636..3c3c63a6 100644 --- a/frontend/src/__test__/components/BarChartGrouped.test.tsx +++ b/frontend/src/__test__/components/BarChartGrouped.test.tsx @@ -1,26 +1,40 @@ import React from 'react'; -import { render, screen, waitFor } from '@testing-library/react'; -import { describe, expect, it, vi } from 'vitest'; +import { render, screen } from '@testing-library/react'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import BarChartGrouped from '../../components/BarChartGrouped'; -import { fetchOpeningsPerYear } from '../../services/OpeningService'; - -vi.mock('../../services/OpeningService', () => ({ - fetchOpeningsPerYear: vi.fn(() => Promise.resolve([ - { group: '2022', key: 'Openings', value: 10 }, - { group: '2023', key: 'Openings', value: 15 }, - ])), +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(), })); -describe('BarChartGrouped component tests', () => { - it('should render loading state while fetching data and clean it after', async () => { - render(); +const queryClient = new QueryClient(); - const element = await waitFor(() => screen.getByText('Loading...')); +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, + }); - expect(element).toBeDefined(); - - expect(fetchOpeningsPerYear).toHaveBeenCalled(); - expect(screen.queryByTestId('bar-chart')).toBeDefined(); - }); + // If you're using useDistrictListQuery, mock it too + (useDistrictListQuery as any).mockReturnValue({ + data: [], + isLoading: false, + }); + render( + + + + ); + + // Check if loading text is displayed + expect(screen.getByText('Loading...')).toBeInTheDocument(); + }); }); diff --git a/frontend/src/__test__/components/Dashboard/Opening/RecentOpeningsDataTable.test.tsx b/frontend/src/__test__/components/Dashboard/Opening/RecentOpeningsDataTable.test.tsx new file mode 100644 index 00000000..d242634a --- /dev/null +++ b/frontend/src/__test__/components/Dashboard/Opening/RecentOpeningsDataTable.test.tsx @@ -0,0 +1,107 @@ +// src/__test__/components/SilvicultureSearch/Openings/OpeningsSearchBar.test.tsx + +import React from "react"; +import { render, screen } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import { vi } from "vitest"; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import PaginationProvider from "../../../../contexts/PaginationProvider"; +import RecentOpeningsDataTable from "../../../../components/Dashboard/Opening/RecentOpeningsDataTable"; +import { MemoryRouter } from "react-router-dom"; + +describe("OpeningsSearchBar", () => { + // Create a new QueryClient instance for each test + const queryClient = new QueryClient(); + const handleCheckboxChange = vi.fn() + const setLoadId = vi.fn() + const toggleSpatial = vi.fn() + const showSpatial = false + const data = { data: [], perPage: 0, totalPages: 0 } + const headers = [] + + it("renders the blank table when data array is empty", () => { + render( + + + + + + + + ); + // Check if the search input field is present with the correct placeholder text + const searchInput = screen.getByText(/Total Search Results: 0/i); + expect(searchInput).toBeInTheDocument(); + }); + + it("renders the table with data", () => { + const data = { data: [{ + "openingId": 114203, + "forestFileId": "TFL47", + "categoryCode": "FTML", + "categoryDescription": null, + "statusCode": "Active", + "statusDescription": "Active", + "cuttingPermitId": "12T", + "cutBlockId": "12-44A", + "orgUnitName": "DCC - Cariboo chilcotin natural resources", + "updateTimestamp": "2021-12-08" + }], perPage: 1, totalPages: 1 } + const headers = [{ + key: 'openingId', + header: 'Opening Id', + selected: false + }, + { + key: 'forestFileId', + header: 'File Id', + selected: false + }, + { + key: 'cuttingPermit', + header: 'Cutting permit', + selected: false + }, + { + key: 'timberMark', + header: 'Timber mark', + selected: false + }, + { + key: 'cutBlock', + header: 'Cut block', + selected: false + },] + render( + + + + + + + + ); + console.log(screen.debug()) + // Check if the search input field is present with the correct placeholder text + const searchInput = screen.getByText(/Total Search Results: 1/i); + expect(searchInput).toBeInTheDocument(); + }); +}); \ No newline at end of file diff --git a/frontend/src/__test__/components/FavoriteButton.test.tsx b/frontend/src/__test__/components/FavoriteButton.test.tsx index 152a8a3a..39e31f27 100644 --- a/frontend/src/__test__/components/FavoriteButton.test.tsx +++ b/frontend/src/__test__/components/FavoriteButton.test.tsx @@ -1,8 +1,9 @@ import React from 'react'; import { render, screen, fireEvent } from '@testing-library/react'; -import { describe, expect, it } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import FavoriteButton from '../../components/FavoriteButton'; import '@testing-library/jest-dom'; +import { on } from 'events'; describe('FavoriteButton Component', () => { const props = { @@ -10,6 +11,8 @@ describe('FavoriteButton Component', () => { kind: 'ghost', size: 'md', fill: 'red', + favorited: false, + onFavoriteChange: vi.fn(), }; it('should render the component with default state', () => { @@ -33,5 +36,12 @@ describe('FavoriteButton Component', () => { const imgElement = screen.getByTestId('favourite-button-icon'); expect(imgElement).toHaveStyle('fill: red'); }); + + it('should call onFavoriteChange with the new favorite state', () => { + render(); + const buttonElement = screen.getByRole('button'); + fireEvent.click(buttonElement); + expect(props.onFavoriteChange).toHaveBeenCalledWith(false); + }); }); diff --git a/frontend/src/__test__/components/MyProfile.test.tsx b/frontend/src/__test__/components/MyProfile.test.tsx index 5e723ce3..078e5ce9 100644 --- a/frontend/src/__test__/components/MyProfile.test.tsx +++ b/frontend/src/__test__/components/MyProfile.test.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import React from 'react'; import { render, act, waitFor, fireEvent, screen } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; import MyProfile from '../../components/MyProfile'; diff --git a/frontend/src/__test__/components/OpeningHistory.test.tsx b/frontend/src/__test__/components/OpeningHistory.test.tsx new file mode 100644 index 00000000..e5be3a72 --- /dev/null +++ b/frontend/src/__test__/components/OpeningHistory.test.tsx @@ -0,0 +1,68 @@ +import React from 'react'; +import { render, act } from '@testing-library/react'; +import { describe, it, expect, vi } from 'vitest'; +import OpeningHistory from '../../components/OpeningHistory'; +import { NotificationProvider } from '../../contexts/NotificationProvider'; +import { deleteOpeningFavorite, fetchOpeningTrends } from '../../services/OpeningFavoriteService'; + +vi.mock('../../services/OpeningFavoriteService', () => ({ + deleteOpeningFavorite: vi.fn(), + fetchOpeningTrends: vi.fn(), +})); + +describe('OpeningHistory Component', () => { + it('renders correctly with given histories', async () => { + (fetchOpeningTrends as vi.Mock).mockReturnValueOnce(Promise.resolve([1, 2])); + let container; + await act(async () => { + ({ container } = render()); + }); + + // Check for the presence of Opening Ids + expect(container.querySelector('div[data-id="1"').innerHTML).toContain('Opening Id 1'); + expect(container.querySelector('div[data-id="2"').innerHTML).toContain('Opening Id 2'); + }); + + it('renders correctly with empty histories', async () => { + (fetchOpeningTrends as vi.Mock).mockReturnValueOnce(Promise.resolve([])); + let container; + await act(async () => { + ({ container } = render()); + }); + + // Select the div with the specific class + const activityHistoryContainer = container.querySelector('.row.activity-history-container.gx-4'); + + // Check if the container is empty + expect(activityHistoryContainer).toBeInTheDocument(); // Ensure the element exists + expect(activityHistoryContainer?.children.length).toBe(0); // Confirm it's empty by checking for no children + }); + + // check if when clicked on the FavoriteButton, the deleteOpeningFavorite function is called + it('should call deleteOpeningFavorite when FavoriteButton is clicked', async () => { + (fetchOpeningTrends as vi.Mock).mockReturnValueOnce(Promise.resolve([1, 2])); + let container; + await act(async () => { + ({ container } = render()); + }); + + const favoriteButton = container.querySelector('.favorite-icon button') + await act(async () => favoriteButton && favoriteButton.click()); + + expect(deleteOpeningFavorite).toHaveBeenCalled(); + }); + + it('should call deleteOpeningFavorite and handle error when FavoriteButton is clicked', async () => { + (fetchOpeningTrends as vi.Mock).mockReturnValueOnce(Promise.resolve([1, 2])); + (deleteOpeningFavorite as vi.Mock).mockRejectedValueOnce(new Error('Failed to delete favorite')); + let container; + await act(async () => { + ({ container } = render()); + }); + + const favoriteButton = container.querySelector('.favorite-icon button') + await act(async () => favoriteButton && favoriteButton.click()); + + expect(deleteOpeningFavorite).toHaveBeenCalled(); + }); +}); diff --git a/frontend/src/__test__/components/OpeningMetricsTab.test.tsx b/frontend/src/__test__/components/OpeningMetricsTab.test.tsx new file mode 100644 index 00000000..08a02b41 --- /dev/null +++ b/frontend/src/__test__/components/OpeningMetricsTab.test.tsx @@ -0,0 +1,110 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import React from 'react'; +import { render, act, waitFor, fireEvent, screen } from '@testing-library/react'; +import OpeningMetricsTab from '../../components/OpeningMetricsTab'; +import { NotificationProvider } from '../../contexts/NotificationProvider'; +import { fetchOpeningTrends } from '../../services/OpeningFavoriteService'; +import { fetchFreeGrowingMilestones, fetchOpeningsPerYear, fetchRecentOpenings } from '../../services/OpeningService'; + +vi.mock('../../services/OpeningFavoriteService', () => ({ + fetchOpeningTrends: vi.fn(), +})); +vi.mock('../../services/OpeningService', async () => { + const actual = await vi.importActual('../../services/OpeningService'); + return { + ...actual, + fetchRecentOpenings: vi.fn(), + fetchOpeningsPerYear: vi.fn(), + fetchFreeGrowingMilestones: vi.fn(), + }; +}); + +describe('OpeningMetricsTab', () => { + beforeEach(() => { + vi.clearAllMocks(); + (fetchRecentOpenings as vi.Mock).mockResolvedValue([{ + id: '123', + openingId: '123', + fileId: '1', + cuttingPermit: '1', + timberMark: '1', + cutBlock: '1', + grossAreaHa: 1, + statusDesc: 'Approved', + categoryDesc: 'Another:Another', + disturbanceStart: '1', + entryTimestamp: '1', + updateTimestamp: '1', + }]); + (fetchOpeningsPerYear as vi.Mock).mockResolvedValue([ + { group: '2022', key: 'Openings', value: 10 }, + { group: '2023', key: 'Openings', value: 15 }, + ]); + (fetchFreeGrowingMilestones as vi.Mock).mockResolvedValue([{ group: '1-5', value: 11 }]); + (fetchOpeningTrends as vi.Mock).mockResolvedValue([1, 2, 3]); + + }); + + it('should render the OpeningMetricsTab component with all sections', async () => { + + await act(async () => render()); + + expect(screen.getByText('Dashboard')).toBeInTheDocument(); + expect(screen.getByText('Manage and track silvicultural information about openings')).toBeInTheDocument(); + expect(screen.getByText('Openings submission trends')).toBeInTheDocument(); + expect(screen.getByText('Check quantity and evolution of openings')).toBeInTheDocument(); + expect(screen.getByText('Track Openings')).toBeInTheDocument(); + expect(screen.getByText('Follow your favourite openings')).toBeInTheDocument(); + expect(screen.getByText('Free growing milestone declarations')).toBeInTheDocument(); + expect(screen.getByText('Check opening standards unit for inspections purposes')).toBeInTheDocument(); + expect(screen.getByText('My recent actions')).toBeInTheDocument(); + expect(screen.getByText('Check your recent requests and files')).toBeInTheDocument(); + }); + + it('should call fetchOpeningTrends and set submissionTrends state', async () => { + + + await act(async () => { + render(); + }); + + await waitFor(() => { + expect(fetchOpeningTrends).toHaveBeenCalled(); + expect(screen.getByText('Opening Id 1')).toBeInTheDocument(); + expect(screen.getByText('Opening Id 2')).toBeInTheDocument(); + expect(screen.getByText('Opening Id 3')).toBeInTheDocument(); + }); + }); + + it('should scroll to "Track Openings" section when scrollTo parameter is "trackOpenings"', async () => { + + const mockScrollIntoView = vi.fn(); + window.HTMLElement.prototype.scrollIntoView = mockScrollIntoView; + + const originalLocation = window.location; + delete window.location; + window.location = { search: '?scrollTo=trackOpenings' } as any; + + await act(async () => render()); + + expect(mockScrollIntoView).toHaveBeenCalledWith({ behavior: 'smooth' }); + + window.location = originalLocation; + }); + + it('should not scroll to "Track Openings" section when scrollTo parameter is not "trackOpenings"', async () => { + + const mockScrollIntoView = vi.fn(); + window.HTMLElement.prototype.scrollIntoView = mockScrollIntoView; + + const originalLocation = window.location; + delete window.location; + window.location = { search: '' } as any; + + await act(async () => render()); + + expect(mockScrollIntoView).not.toHaveBeenCalled(); + + window.location = originalLocation; + }); +}); \ No newline at end of file diff --git a/frontend/src/__test__/components/OpeningsTab.test.tsx b/frontend/src/__test__/components/OpeningsTab.test.tsx new file mode 100644 index 00000000..c81f3901 --- /dev/null +++ b/frontend/src/__test__/components/OpeningsTab.test.tsx @@ -0,0 +1,50 @@ +// src/__test__/components/SilvicultureSearch/Openings/OpeningsSearchBar.test.tsx + +import React from "react"; +import { render, screen } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import { vi } from "vitest"; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import OpeningsTab from "../../../src/components/OpeningsTab"; +import { Provider } from "react-redux"; +import store from "../../store"; + +describe("OpeningsTab", () => { + // Create a new QueryClient instance for each test + const queryClient = new QueryClient(); + const showSpatial = false + const setShowSpatial = vi.fn() + + it("renders the component successfully", () => { + render( + + + + + + ); + // Check if the component is present with the correct text + const searchInput = screen.getByText(/Track the history of openings you have looked at and check spatial information by selecting the openings in the table below/i); + expect(searchInput).toBeInTheDocument(); + }); + + it("shows the spatial area with Hide Spatial Button", () => { + render( + + + + + + ); + console.log(screen.debug()) + // Check if the component is present with the correct text + const searchInput = screen.getByRole('button', { name: /Hide Spatial/i }); + expect(searchInput).toBeInTheDocument(); + }); +}); \ No newline at end of file diff --git a/frontend/src/__test__/components/SilvicultureSearch/Openings/AdvancedSearchDropdown.test.tsx b/frontend/src/__test__/components/SilvicultureSearch/Openings/AdvancedSearchDropdown.test.tsx new file mode 100644 index 00000000..994b8faf --- /dev/null +++ b/frontend/src/__test__/components/SilvicultureSearch/Openings/AdvancedSearchDropdown.test.tsx @@ -0,0 +1,68 @@ +import { render, screen, fireEvent } from "@testing-library/react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import "@testing-library/jest-dom"; +import AdvancedSearchDropdown from "../../../../components/SilvicultureSearch/Openings/AdvancedSearchDropdown"; +import { useOpeningFiltersQuery } from "../../../../services/queries/search/openingQueries"; +import { useOpeningsSearch } from "../../../../contexts/search/OpeningsSearch"; +import React from "react"; + +// Mocking the toggleShowFilters function +const toggleShowFilters = vi.fn(); + +// Mocking useOpeningFiltersQuery to return mock data for filters +vi.mock("../../../../services/queries/search/openingQueries", () => ({ + useOpeningFiltersQuery: vi.fn(), +})); + +// Mocking useOpeningsSearch to return the necessary functions and state +vi.mock("../../../../contexts/search/OpeningsSearch", () => ({ + useOpeningsSearch: vi.fn(), +})); + +describe("AdvancedSearchDropdown", () => { + beforeEach(() => { + // Mock data to return for the filters query + (useOpeningFiltersQuery as jest.Mock).mockReturnValue({ + data: { + categories: ["FTML", "CONT"], + orgUnits: ["DCK", "DCR"], + dateTypes: ["Disturbance", "Free Growing"], + }, + isLoading: false, + isError: false, + }); + + // Mock implementation of useOpeningsSearch context + (useOpeningsSearch as jest.Mock).mockReturnValue({ + filters: { + openingFilters: [], + orgUnit: [], + category: [], + clientAcronym: "", + clientLocationCode: "", + cutBlock: "", + cuttingPermit: "", + timberMark: "", + dateType: "", + startDate: null, + endDate: null, + status: [], + }, + setFilters: vi.fn(), + clearFilters: vi.fn(), + }); + }); + + it("displays an error message if there is an error", () => { + (useOpeningFiltersQuery as jest.Mock).mockReturnValue({ + isLoading: false, + isError: true, + data: null, + }); + + render(); + expect( + screen.getByText("There was an error while loading the advanced filters.") + ).toBeInTheDocument(); + }); +}); \ No newline at end of file diff --git a/frontend/src/__test__/components/SilvicultureSearch/Openings/OpeningSearchBar.test.tsx b/frontend/src/__test__/components/SilvicultureSearch/Openings/OpeningSearchBar.test.tsx new file mode 100644 index 00000000..3f99fe56 --- /dev/null +++ b/frontend/src/__test__/components/SilvicultureSearch/Openings/OpeningSearchBar.test.tsx @@ -0,0 +1,56 @@ +// src/__test__/components/SilvicultureSearch/Openings/OpeningsSearchBar.test.tsx + +import React from "react"; +import { render, screen } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import OpeningsSearchBar from "../../../../components/SilvicultureSearch/Openings/OpeningsSearchBar"; +import { vi } from "vitest"; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { useOpeningsSearch } from "../../../../contexts/search/OpeningsSearch"; + +// Mock the useOpeningsSearch context to avoid rendering errors +vi.mock("../../../../contexts/search/OpeningsSearch", () => ({ + useOpeningsSearch: vi.fn().mockReturnValue({ + filters: [], + clearFilters: vi.fn(), + searchTerm: "", + setSearchTerm: vi.fn(), + }), +})); + +describe("OpeningsSearchBar", () => { + // Create a new QueryClient instance for each test + const queryClient = new QueryClient(); + + it("renders the search input with the correct placeholder", () => { + render( + + {}} /> + + ); + + // Check if the search input field is present with the correct placeholder text + const searchInput = screen.getByPlaceholderText( + "Search by opening ID, opening number, timber mark or file ID" + ); + expect(searchInput).toBeInTheDocument(); + }); + + it("should call the onSearchClick function when the search button is clicked", () => { + // Create a mock function to pass as a prop + const onSearchClick = vi.fn(); + + render( + + + + ); + + // Click the search button + const searchButton = screen.getAllByRole("button", { name: "Search" })[1]; + searchButton.click(); + + // Check if the onSearchClick function was called + expect(onSearchClick).toHaveBeenCalled(); + }); +}); \ No newline at end of file diff --git a/frontend/src/__test__/components/ThemeToggle.test.tsx b/frontend/src/__test__/components/ThemeToggle.test.tsx new file mode 100644 index 00000000..41cdceea --- /dev/null +++ b/frontend/src/__test__/components/ThemeToggle.test.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import { describe, expect, it, vi } from 'vitest'; +import ThemeToggle from '../../components/ThemeToggle'; + +describe('Theme toggle component tests', () => { + it('should render correctly', () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it('should toggle theme when button is clicked', () => { + const { container } = render(); + const button = container.querySelector('button'); + button?.click(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/frontend/src/__test__/components/__snapshots__/ThemeToggle.test.tsx.snap b/frontend/src/__test__/components/__snapshots__/ThemeToggle.test.tsx.snap new file mode 100644 index 00000000..2c112feb --- /dev/null +++ b/frontend/src/__test__/components/__snapshots__/ThemeToggle.test.tsx.snap @@ -0,0 +1,61 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Theme toggle component tests > should render correctly 1`] = ` +
+
+
+ +
+
+
+`; + +exports[`Theme toggle component tests > should toggle theme when button is clicked 1`] = ` +
+
+
+ +
+
+
+`; diff --git a/frontend/src/__test__/contexts/AuthProvider.test.tsx b/frontend/src/__test__/contexts/AuthProvider.test.tsx index 3430ccca..7106ad23 100644 --- a/frontend/src/__test__/contexts/AuthProvider.test.tsx +++ b/frontend/src/__test__/contexts/AuthProvider.test.tsx @@ -91,7 +91,7 @@ describe('AuthProvider', () => { it('should handle login correctly', async () => { setAuthCookies(sampleAuthToken); const provider = 'idir'; - const envProvider = 'TEST-IDIR'; + const envProvider = `${env.VITE_ZONE ?? 'DEV'}-IDIR`; const TestComponent = () => { const { login } = useGetAuth(); diff --git a/frontend/src/__test__/contexts/NotificationProvider.test.tsx b/frontend/src/__test__/contexts/NotificationProvider.test.tsx new file mode 100644 index 00000000..320bda9b --- /dev/null +++ b/frontend/src/__test__/contexts/NotificationProvider.test.tsx @@ -0,0 +1,128 @@ +import React from 'react'; +import { render, screen, act } from '@testing-library/react'; +import { describe, it, expect, vi } from 'vitest'; +import { NotificationProvider, useNotification } from '../../contexts/NotificationProvider'; +import { NotificationContent } from '../../types/NotificationType'; +import { ActionableNotification } from '@carbon/react'; + +// Mock the ActionableNotification component +vi.mock('@carbon/react', () => ({ + ActionableNotification: ({ title, subtitle, onClose, onActionButtonClick }: any) => ( +
+
{title}
+
{subtitle}
+ + +
+ ) +})); + +const onCloseMock = vi.fn(); + +const TestComponent = () => { + const { displayNotification } = useNotification(); + return ( + + ); +}; + +describe('NotificationProvider', () => { + it('should display notification when displayNotification is called', () => { + render( + + + + ); + + act(() => { + screen.getByText('Show Notification').click(); + }); + + expect(screen.getByText('Test Title')).toBeInTheDocument(); + expect(screen.getByText('Test Subtitle')).toBeInTheDocument(); + expect(screen.getByText('Close')).toBeInTheDocument(); + expect(screen.getByText('Action')).toBeInTheDocument(); + }); + + it('should hide notification when close button is clicked', () => { + render( + + + + ); + + act(() => { + screen.getByText('Show Notification').click(); + }); + + act(() => { + screen.getByText('Close').click(); + }); + + expect(screen.queryByText('Test Title')).not.toBeInTheDocument(); + expect(screen.queryByText('Test Subtitle')).not.toBeInTheDocument(); + }); + + it('should call onClose and hide notification when action button is clicked', () => { + + + render( + + + + ); + + act(() => { + screen.getByText('Show Notification').click(); + }); + + act(() => { + screen.getByText('Action').click(); + }); + + expect(onCloseMock).toHaveBeenCalled(); + expect(screen.queryByText('Test Title')).not.toBeInTheDocument(); + expect(screen.queryByText('Test Subtitle')).not.toBeInTheDocument(); + }); + + it('should hide notification after dismissIn time', async () => { + vi.useFakeTimers(); // Use fake timers + + await act(async () => render( + + + + )); + + await act(async () => { + screen.getByText('Show Notification').click(); + }); + + expect(screen.getByText('Test Title')).toBeInTheDocument(); + expect(screen.getByText('Test Subtitle')).toBeInTheDocument(); + + await act(async () => vi.advanceTimersByTime(1111)); // Fast-forward time by 1 second + + expect(screen.queryByText('Test Title')).not.toBeInTheDocument(); + expect(screen.queryByText('Test Subtitle')).not.toBeInTheDocument + + }); + + + afterEach(() => { + vi.useRealTimers(); // Restore real timers after each test + }); +}); \ No newline at end of file diff --git a/frontend/src/__test__/contexts/OpeningsSearch.test.tsx b/frontend/src/__test__/contexts/OpeningsSearch.test.tsx new file mode 100644 index 00000000..810652a8 --- /dev/null +++ b/frontend/src/__test__/contexts/OpeningsSearch.test.tsx @@ -0,0 +1,56 @@ +// OpeningsSearchProvider.test.tsx +import React from 'react'; +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { render, screen, fireEvent } from '@testing-library/react'; +import { OpeningsSearchProvider, useOpeningsSearch } from '../../contexts/search/OpeningsSearch'; + +const TestComponent: React.FC = () => { + const { filters, setFilters, searchTerm, setSearchTerm, clearFilters, clearIndividualField } = useOpeningsSearch(); + + return ( +
+

{searchTerm}

+

{String(filters.startDate)}

+ + + + +
+ ); +}; + +describe('OpeningsSearchProvider', () => { + beforeEach(() => { + render( + + + + ); + }); + + it('should initialize with default values', () => { + expect(screen.getByTestId('searchTerm').textContent).toBe(''); + expect(screen.getByTestId('startDate').textContent).toBe('null'); + }); + + it('should update searchTerm', () => { + fireEvent.click(screen.getByTestId('setSearchTerm')); + expect(screen.getByTestId('searchTerm').textContent).toBe('test search'); + }); + + it('should set and then clear filters', () => { + fireEvent.click(screen.getByTestId('setFilters')); + expect(screen.getByTestId('startDate').textContent).not.toBe('null'); + + fireEvent.click(screen.getByTestId('clearFilters')); + expect(screen.getByTestId('startDate').textContent).toBe('null'); + }); + + it('should clear individual field', () => { + fireEvent.click(screen.getByTestId('setFilters')); + expect(screen.getByTestId('startDate').textContent).not.toBe('null'); + + fireEvent.click(screen.getByTestId('clearStartDate')); + expect(screen.getByTestId('startDate').textContent).toBe('null'); + }); +}); diff --git a/frontend/src/__test__/screens/Help.test.tsx b/frontend/src/__test__/screens/Help.test.tsx deleted file mode 100644 index ff823dc2..00000000 --- a/frontend/src/__test__/screens/Help.test.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React from 'react'; -import { render, screen, act } from '@testing-library/react'; -import { MemoryRouter } from 'react-router-dom'; -import Help from '../../screens/Help'; -import { describe, expect, it } from 'vitest'; - -describe('Help component test cases', () => { - it('should renders the help page header', () => { - render(, { wrapper: MemoryRouter }); - const headerElement = screen.getByText('Help Page'); - expect(headerElement).not.toBeNull(); - }); - - it('should renders the help page content', () => { - render(, { wrapper: MemoryRouter }); - const contentElement = screen.getByText(/Welcome to the Help Page/i); - expect(contentElement).not.toBeNull(); - }); - - it('should navigates back to home page on button click', () => { - act(() => { - render(, { wrapper: MemoryRouter }); - }); - - act(() => { - const backButton = screen.getByRole('button', { name: /Back to Home/i }); - backButton.click(); - }); - - // Add assertions to test if navigation to the home page occurs - expect(location.pathname).toBe('/'); - }); -}); diff --git a/frontend/src/__test__/screens/Opening.test.tsx b/frontend/src/__test__/screens/Opening.test.tsx index 0535dc88..4ac7b4a3 100644 --- a/frontend/src/__test__/screens/Opening.test.tsx +++ b/frontend/src/__test__/screens/Opening.test.tsx @@ -6,21 +6,21 @@ import PaginationContext from '../../contexts/PaginationContext'; import { BrowserRouter } from 'react-router-dom'; import * as redux from 'react-redux'; import { RecentOpening } from '../../types/RecentOpening'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +// Mock data and services const data = { - "activityType": "Update", - "openingId": "1541297", - "statusCode": "APP", - "statusDescription": "Approved", - "lastUpdatedLabel": "1 minute ago", - "lastUpdated": "2024-05-16T19:59:21.635Z" + activityType: "Update", + openingId: "1541297", + statusCode: "APP", + statusDescription: "Approved", + lastUpdatedLabel: "1 minute ago", + lastUpdated: "2024-05-16T19:59:21.635Z" }; vi.mock('../../services/SecretsService', () => ({ getWmsLayersWhitelistUsers: vi.fn(() => [ - { - userName: 'TEST' - } + { userName: 'TEST' } ]) })); @@ -46,10 +46,7 @@ vi.mock('../../services/OpeningService', () => ({ { group: '2023', key: 'Openings', value: 15 }, ])), fetchFreeGrowingMilestones: vi.fn(() => Promise.resolve([ - { - group: '1-5', - value: 11 - } + { group: '1-5', value: 11 } ])), fetchRecentActions: vi.fn(() => [ { @@ -73,10 +70,11 @@ const state = { vi.spyOn(redux, 'useSelector') .mockImplementation((callback) => callback(state)); +// Pagination context mock const rows: RecentOpening[] = [{ id: '123', openingId: '123', - fileId: '1', + forestFileId: '1', cuttingPermit: '1', timberMark: '1', cutBlock: '1', @@ -87,7 +85,7 @@ const rows: RecentOpening[] = [{ entryTimestamp: '1', updateTimestamp: '1', }]; - + const paginationValueMock = { getCurrentData: () => rows, currentPage: 0, @@ -99,20 +97,30 @@ const paginationValueMock = { setInitialItemsPerPage: vi.fn(), }; +// Create a query client for testing +const createTestQueryClient = () => new QueryClient({ + defaultOptions: { + queries: { + retry: false, // Disable retries for test stability + }, + }, +}); + describe('Opening screen test cases', () => { - it('should renders Opening Page Title component', async () => { + it('should render Opening Page Title component', async () => { + const queryClient = createTestQueryClient(); + const { getByTestId } = render( - - - - - + + + + + + + ); const pageTitleComp = await waitFor(() => getByTestId('opening-pagetitle')); expect(pageTitleComp).toBeDefined(); - - //const subtitle = 'Create, manage or check opening information'; - //expect(screen.getByText(subtitle)).toBeDefined(); }); }); diff --git a/frontend/src/__test__/screens/Reports.test.tsx b/frontend/src/__test__/screens/Reports.test.tsx deleted file mode 100644 index 6996653a..00000000 --- a/frontend/src/__test__/screens/Reports.test.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import { describe, expect, it } from 'vitest'; -import Reports from '../../screens/Reports'; - -describe('Reports', () => { - it('should render the reports page title', () => { - render(); - const titleElement = screen.getByText(/Reports Page/i); - expect(titleElement).toBeDefined(); - }); - - it('should render the form sample title', () => { - render(); - const formTitleElement = screen.getByText(/Form Sample/i); - expect(formTitleElement).toBeDefined(); - }); - - it('should render the date picker component', () => { - render(); - const datePickerElement = screen.getByLabelText(/Start date/i); - expect(datePickerElement).toBeDefined(); - }); - - it('should renders the dropdown component', () => { - render(); - const dropdownElements = screen.getAllByLabelText(/Select Fruit from Dropdown/i); - expect(dropdownElements.length).toBe(8); - }); - - it('renders the modal button', () => { - render(); - const modalButtonElement = screen.getByText(/Launch modal/i); - expect(modalButtonElement).toBeDefined(); - }); - - it('renders the table headers', () => { - render(); - const tableHeaders = ['Name', 'Rule', 'Status', 'Other', 'Example']; - tableHeaders.forEach((header) => { - const headerElement = screen.getByText(header); - expect(headerElement).toBeDefined(); - }); - }); - - it('renders the table rows and cells', () => { - render(); - const tableRows = screen.getAllByRole('row'); - // Excluding the header row - expect(tableRows.length).toBe(8); - - // Example: Check for specific cell content - const cellContent = screen.getByText('Load Balancer 1'); - expect(cellContent).toBeDefined(); - }); -}); diff --git a/frontend/src/__test__/services/OpeningFavoriteService.test.ts b/frontend/src/__test__/services/OpeningFavoriteService.test.ts new file mode 100644 index 00000000..d24962fa --- /dev/null +++ b/frontend/src/__test__/services/OpeningFavoriteService.test.ts @@ -0,0 +1,116 @@ +import { describe, it, expect, vi } from 'vitest'; +import axios from 'axios'; +import { fetchOpeningTrends} from '../../services/OpeningFavoriteService'; +import { getAuthIdToken } from '../../services/AuthService'; +import { env } from '../../env'; +import { fetchOpeningTrends, setOpeningFavorite, deleteOpeningFavorite } from '../../services/OpeningFavoriteService'; + +vi.mock('axios'); +vi.mock('../../services/AuthService'); + +describe('OpeningFavoriteService', () => { + const backendUrl = env.VITE_BACKEND_URL; + const authToken = 'test-token'; + + beforeEach(() => { + vi.clearAllMocks(); + (getAuthIdToken as vi.Mock).mockReturnValue(authToken); + }); + + it('should fetch submission trends successfully', async () => { + const mockData = [1, 2, 3]; + (axios.get as vi.Mock).mockResolvedValue({ data: mockData }); + + const result = await fetchOpeningTrends(); + + expect(axios.get).toHaveBeenCalledWith(`${backendUrl}/api/openings/favorites`, { + headers: { Authorization: `Bearer ${authToken}` } + }); + expect(result).toEqual(mockData); + }); + + it('should fetch submission trends with empty results', async () => { + const mockData = []; + (axios.get as vi.Mock).mockResolvedValue({ data: mockData }); + + const result = await fetchOpeningTrends(); + + expect(axios.get).toHaveBeenCalledWith(`${backendUrl}/api/openings/favorites`, { + headers: { Authorization: `Bearer ${authToken}` } + }); + expect(result).toEqual(mockData); + }); + + it('should handle error while fetching submission trends', async () => { + (axios.get as vi.Mock).mockRejectedValue(new Error('Network Error')); + + await expect(fetchOpeningTrends()).rejects.toThrow('Network Error'); + }); + + it('should fetch submission trends successfully', async () => { + const mockData = [1, 2, 3]; + (axios.get as vi.Mock).mockResolvedValue({ data: mockData }); + + const result = await fetchOpeningTrends(); + + expect(axios.get).toHaveBeenCalledWith(`${backendUrl}/api/openings/favorites`, { + headers: { Authorization: `Bearer ${authToken}` } + }); + expect(result).toEqual(mockData); + }); + + it('should fetch submission trends with empty results', async () => { + const mockData = []; + (axios.get as vi.Mock).mockResolvedValue({ data: mockData }); + + const result = await fetchOpeningTrends(); + + expect(axios.get).toHaveBeenCalledWith(`${backendUrl}/api/openings/favorites`, { + headers: { Authorization: `Bearer ${authToken}` } + }); + expect(result).toEqual(mockData); + }); + + it('should handle error while fetching submission trends', async () => { + (axios.get as vi.Mock).mockRejectedValue(new Error('Network Error')); + + await expect(fetchOpeningTrends()).rejects.toThrow('Network Error'); + }); + + it('should set an opening as favorite successfully', async () => { + const openingId = 1; + (axios.put as vi.Mock).mockResolvedValue({ status: 202 }); + + await setOpeningFavorite(openingId); + + expect(axios.put).toHaveBeenCalledWith(`${backendUrl}/api/openings/favorites/${openingId}`, null, { + headers: { Authorization: `Bearer ${authToken}` } + }); + }); + + it('should throw an error if setting an opening as favorite fails', async () => { + const openingId = 1; + (axios.put as vi.Mock).mockResolvedValue({ status: 500 }); + + await expect(setOpeningFavorite(openingId)).rejects.toThrow('Failed to set favorite opening. Status code: 500'); + }); + + it('should delete a favorite opening successfully', async () => { + const openingId = 1; + (axios.delete as vi.Mock).mockResolvedValue({ status: 204 }); + + await deleteOpeningFavorite(openingId); + + expect(axios.delete).toHaveBeenCalledWith(`${backendUrl}/api/openings/favorites/${openingId}`, { + headers: { Authorization: `Bearer ${authToken}` } + }); + }); + + it('should throw an error if deleting a favorite opening fails', async () => { + const openingId = 1; + (axios.delete as vi.Mock).mockResolvedValue({ status: 500 }); + + await expect(deleteOpeningFavorite(openingId)).rejects.toThrow('Failed to remove favorite opening. Status code: 500'); + }); + +}); \ No newline at end of file diff --git a/frontend/src/__test__/services/OpeningService.test.ts b/frontend/src/__test__/services/OpeningService.test.ts new file mode 100644 index 00000000..6dd4f320 --- /dev/null +++ b/frontend/src/__test__/services/OpeningService.test.ts @@ -0,0 +1,146 @@ +import { describe, it, expect, vi } from 'vitest'; +import axios from 'axios'; +import { + fetchRecentOpenings, + fetchOpeningsPerYear, + fetchFreeGrowingMilestones, + fetchRecentActions +} from '../../services/OpeningService'; +import { getAuthIdToken } from '../../services/AuthService'; +import { env } from '../../env'; + +vi.mock('axios'); +vi.mock('../../services/AuthService'); + +describe('OpeningService', () => { + const backendUrl = env.VITE_BACKEND_URL; + const authToken = 'test-token'; + + beforeEach(() => { + vi.clearAllMocks(); + (getAuthIdToken as vi.Mock).mockReturnValue(authToken); + }); + + describe('fetchRecentOpenings', () => { + it('should fetch recent openings successfully', async () => { + const mockData = { + data: [ + { + openingId: 1, + forestFileId: '123', + cuttingPermit: '456', + timberMark: '789', + cutBlock: 'A', + grossAreaHa: 10, + status: { description: 'Active' }, + category: { description: 'Category1' }, + disturbanceStart: '2023-01-01', + entryTimestamp: '2023-01-01T00:00:00Z', + updateTimestamp: '2023-01-02T00:00:00Z' + } + ] + }; + (axios.get as vi.Mock).mockResolvedValue({ status: 200, data: mockData }); + + const result = await fetchRecentOpenings(); + + expect(axios.get).toHaveBeenCalledWith(`${backendUrl}/api/openings/recent-openings?page=0&perPage=100`, { + headers: { Authorization: `Bearer ${authToken}` } + }); + expect(result).toEqual([ + { + id: '1', + openingId: '1', + forestFileId: '123', + cuttingPermit: '456', + timberMark: '789', + cutBlock: 'A', + grossAreaHa: '10', + status: 'Active', + category: 'Category1', + disturbanceStart: '2023-01-01', + entryTimestamp: '2023-01-01', + updateTimestamp: '2023-01-02' + } + ]); + }); + + it('should handle error while fetching recent openings', async () => { + (axios.get as vi.Mock).mockRejectedValue(new Error('Network Error')); + + await expect(fetchRecentOpenings()).rejects.toThrow('Network Error'); + }); + }); + + describe('fetchOpeningsPerYear', () => { + it('should fetch openings per year successfully', async () => { + const mockData = [ + { monthName: 'January', amount: 10 }, + { monthName: 'February', amount: 20 } + ]; + (axios.get as vi.Mock).mockResolvedValue({ data: mockData }); + + 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`, { + headers: { Authorization: `Bearer ${authToken}` } + }); + expect(result).toEqual([ + { group: 'Openings', key: 'January', value: 10 }, + { group: 'Openings', key: 'February', value: 20 } + ]); + }); + + it('should handle error while fetching openings per year', async () => { + (axios.get as vi.Mock).mockRejectedValue(new Error('Network Error')); + + await expect(fetchOpeningsPerYear({})).rejects.toThrow('Network Error'); + }); + }); + + describe('fetchFreeGrowingMilestones', () => { + it('should fetch free growing milestones successfully', async () => { + const mockData = [ + { label: 'Milestone1', amount: 10 }, + { label: 'Milestone2', amount: 20 } + ]; + (axios.get as vi.Mock).mockResolvedValue({ data: mockData }); + + const props = { orgUnitCode: '001', clientNumber: '123', entryDateStart: '2023-01-01', entryDateEnd: '2023-12-31' }; + const result = await fetchFreeGrowingMilestones(props); + + expect(axios.get).toHaveBeenCalledWith(`${backendUrl}/api/dashboard-metrics/free-growing-milestones?orgUnitCode=001&clientNumber=123&entryDateStart=2023-01-01&entryDateEnd=2023-12-31`, { + headers: { Authorization: `Bearer ${authToken}` } + }); + expect(result).toEqual([ + { group: 'Milestone1', value: 10 }, + { group: 'Milestone2', value: 20 } + ]); + }); + + it('should handle error while fetching free growing milestones', async () => { + (axios.get as vi.Mock).mockRejectedValue(new Error('Network Error')); + + await expect(fetchFreeGrowingMilestones({})).rejects.toThrow('Network Error'); + }); + }); + + describe('fetchRecentActions', () => { + it('should fetch recent actions successfully', () => { + const result = fetchRecentActions(); + + expect(result).toEqual([ + { + activityType: 'Update', + openingId: '1541297', + statusCode: 'APP', + statusDescription: 'Approved', + lastUpdatedLabel: '1 minute ago', + lastUpdated: '2024-05-16T19:59:21.635Z' + } + ]); + }); + + }); +}); \ No newline at end of file diff --git a/frontend/src/__test__/services/search/openings.test.tsx b/frontend/src/__test__/services/search/openings.test.tsx new file mode 100644 index 00000000..6ddbf33e --- /dev/null +++ b/frontend/src/__test__/services/search/openings.test.tsx @@ -0,0 +1,109 @@ +// fetchOpenings.test.ts +import axios from "axios"; +import "@testing-library/jest-dom"; +import { fetchOpenings, OpeningFilters } from "../../../services/search/openings"; +import { getAuthIdToken } from "../../../services/AuthService"; +import { createDateParams } from "../../../utils/searchUtils"; +import { describe, it, beforeEach, afterEach, vi, expect } from "vitest"; + +// Mock dependencies +vi.mock("axios"); +vi.mock("../../../services/AuthService"); +vi.mock("../../../utils/searchUtils"); + +// Define mocked functions and modules +const mockedAxios = axios as vi.Mocked; +const mockedGetAuthIdToken = getAuthIdToken as vi.Mock; +const mockedCreateDateParams = createDateParams as vi.Mock; + +// Sample filters +const sampleFilters: OpeningFilters = { + searchInput: "", + startDate: "2024-11-19", + endDate: "2024-11-21", + orgUnit: ["DCC", "DCK", "DCR"], + category: ["EXCLU", "CONT"], + status: ["DFT", "APP"], + clientAcronym: "12", + blockStatus: "", + cutBlock: "L", + cuttingPermit: "PC", + timberMark: "123", + dateType: "Disturbance", + openingFilters: ["Openings created by me", "Submitted to FRPA section 108"], + blockStatuses: [], + page: 1, + perPage: 5, +}; + +// Mock response from the backend API +const mockApiResponse = { + data: { + pageIndex: 0, + perPage: 5, + totalPages: 100, + hasNextPage: false, + data: [ + { + openingId: 9100129, + openingNumber: "98", + cuttingPermitId: "S", + timberMark: "W1729S", + cutBlockId: "06-03", + orgUnitCode: "DPG", + orgUnitName: "Prince George Natural Resource District", + entryUserId: "Datafix107808", + statusCode: "APP", + statusDescription: "Approved", + categoryCode: "FTML", + categoryDescription: "Forest Tenure - Major Licensee", + }, + ], + }, +}; + +describe("fetchOpenings", () => { + beforeEach(() => { + mockedGetAuthIdToken.mockReturnValue("mocked-token"); + mockedCreateDateParams.mockReturnValue({ + dateStartKey: "disturbanceStartDate", + dateEndKey: "disturbanceEndDate", + }); + mockedAxios.get.mockResolvedValue(mockApiResponse); // Mock API response + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + it("should fetch openings with the correct parameters and return flattened data", async () => { + const result = await fetchOpenings(sampleFilters); + const expectedToken = 'mocked-token'; + // Verify that axios was called with the correct URL and headers + expect(mockedAxios.get).toHaveBeenCalledWith( + expect.stringContaining("/api/opening-search?"), + expect.objectContaining({ + headers: { + Authorization: `Bearer ${expectedToken}`, + }, + }) + ); + + // Check if the result data matches the expected flattened structure + expect(result.data[0].openingId).toEqual(9100129); + }); + + it("should handle an empty response data array gracefully", async () => { + mockedAxios.get.mockResolvedValueOnce({ data: { data: [] } }); + const result = await fetchOpenings(sampleFilters); + + // Ensure the function returns an empty array when the response is empty + expect(result.data).toEqual([]); + }); + + it("should throw an error when the API request fails", async () => { + mockedAxios.get.mockRejectedValueOnce(new Error("Network error")); + + await expect(fetchOpenings(sampleFilters)).rejects.toThrow("Network error"); + }); +}); diff --git a/frontend/src/__test__/utils/DateUtils.test.ts b/frontend/src/__test__/utils/DateUtils.test.ts new file mode 100644 index 00000000..f5e8acd3 --- /dev/null +++ b/frontend/src/__test__/utils/DateUtils.test.ts @@ -0,0 +1,38 @@ +import { describe, it, expect } from 'vitest'; +import { formatDate, dateStringToISO } from '../../utils/DateUtils'; + +describe('formatDate', () => { + it('should format a valid date string to "Month Day, Year"', () => { + const result = formatDate('2023-11-05'); + expect(result).toBe('November 5, 2023'); + }); + + it('should return "--" for an empty string', () => { + const result = formatDate(''); + expect(result).toBe('--'); + }); + + it('should return "--" for a null value', () => { + // @ts-ignore + const result = formatDate(null); + expect(result).toBe('--'); + }); +}); + +describe('dateStringToISO', () => { + it('should convert a date string to ISO format', () => { + const result = dateStringToISO('2023-11-05'); + expect(result).toBe('2023-11-05T00:00:00.000Z'); + }); + + it('should return an empty string for an invalid date', () => { + const result = dateStringToISO(''); + expect(result).toBe(''); + }); + + it('should return an empty string for a null value', () => { + // @ts-ignore + const result = dateStringToISO(null); + expect(result).toBe(''); + }); +}); diff --git a/frontend/src/__test__/views/LoginOrgSelection.test.tsx b/frontend/src/__test__/views/LoginOrgSelection.test.tsx deleted file mode 100644 index a003fdc7..00000000 --- a/frontend/src/__test__/views/LoginOrgSelection.test.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import { describe, expect, it, vi } from 'vitest'; -import { Provider } from 'react-redux'; -import store from '../../store'; -import LoginOrgSelection from '../../views/LoginOrgSelection'; -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { UserClientRolesType } from '../../types/UserRoleType'; -import * as redux from 'react-redux'; - -vi.mock('../../services/TestService', () => ({ - getForestClientByNumberOrAcronym: vi.fn(() => [ - { - clientNumber: '00012797', - clientName: 'MINISTRY OF FORESTS', - legalFirstName: '', - legalMiddleName: '', - clientStatusCode: { code: 'ACT', description: 'Active' }, - clientTypeCode: { code: 'F', description: 'Ministry of Forests and Range' }, - acronym: 'MOF' - }, - ]), -})); - -const clientRoles: UserClientRolesType[] = [ - { - clientId: '00012797', - roles: ['ONE', 'TWO', 'THREE'], - clientName: 'MINISTRY OF FORESTS' - } -]; - -const state = { - userDetails: { - id: 1, - name: 'User', - user: { - firstName: 'John', - lastName: 'Doe', - providerUsername: 'johndoe123', - clientRoles: clientRoles - }, - loading: false, - error: null - }, -}; - -vi.spyOn(redux, 'useSelector') - .mockImplementation((callback) => callback(state)); - -describe('LoginOrgSelection', () => { - it('renders organization selection view with user details', () => { - const qc = new QueryClient(); - - render( - - - - - - ); - - // Check if elements are rendered correctly - expect(screen.getByText('Organization selection')).toBeDefined(); - expect(screen.getByText('John Doe (johndoe123) select which organization you\'re representing.')).toBeDefined(); - }); -}); diff --git a/frontend/src/actions/selectedClientRolesActions.ts b/frontend/src/actions/selectedClientRolesActions.ts deleted file mode 100644 index aa0ec954..00000000 --- a/frontend/src/actions/selectedClientRolesActions.ts +++ /dev/null @@ -1,16 +0,0 @@ -// selectedClientRolesActions.js -import { SET_SELECTED_CLIENT_ROLES } from '../constants/selectedClientRolesConstants'; -import { AppDispatch } from '../store'; -import { UserClientRolesType } from '../types/UserRoleType'; - -export interface SetSelectedClientRolesAction { - type: typeof SET_SELECTED_CLIENT_ROLES; - payload: UserClientRolesType; -} - -export const setSelectedClientRoles = (selectedClientRoles: UserClientRolesType) => (dispatch: AppDispatch) => { - dispatch({ - type: SET_SELECTED_CLIENT_ROLES, - payload: selectedClientRoles - }); -}; diff --git a/frontend/src/actions/userAction.ts b/frontend/src/actions/userAction.ts deleted file mode 100644 index 87d6369d..00000000 --- a/frontend/src/actions/userAction.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { - USER_DETAILS_REQUEST, - USER_DETAILS_SUCCESS, - USER_DETAILS_FAIL, - SET_CLIENT_ROLES -} from '../constants/userConstants'; -import { AppDispatch } from '../store'; -import { UserClientRolesType } from '../types/UserRoleType'; -import { useGetAuth } from '../contexts/AuthProvider'; - -const FAM_LOGIN_USER = 'famLoginUser'; - -export const getUserDetails = () => async (dispatch: AppDispatch) => { - try { - dispatch({ - type: USER_DETAILS_REQUEST - }); - //first call the isCurrent and only after that extract the JSON - const { isLoggedIn } = useGetAuth(); - - const userJSON = localStorage.getItem(FAM_LOGIN_USER); // Retrieve the JSON string from local storage - const user = userJSON ? JSON.parse(userJSON) : null; // Parse the JSON string to a JavaScript object - - dispatch({ - type: USER_DETAILS_SUCCESS, - payload: { ...user, isLoggedIn } - }); - } catch (error) { - dispatch({ - type: USER_DETAILS_FAIL, - payload: { error: error } - }); - } -}; - -export const setClientRoles = (clientRoles:UserClientRolesType[]) => (dispatch: AppDispatch) => { - dispatch({ - type: SET_CLIENT_ROLES, - payload: clientRoles - }); -}; diff --git a/frontend/src/amplifyconfiguration.ts b/frontend/src/amplifyconfiguration.ts index 31a482f0..0598eda2 100644 --- a/frontend/src/amplifyconfiguration.ts +++ b/frontend/src/amplifyconfiguration.ts @@ -3,7 +3,8 @@ import { env } from './env'; const ZONE = env.VITE_ZONE.toLocaleLowerCase(); const redirectUri = window.location.origin; const logoutDomain = `https://logon${ZONE === "prod"?'':'test'}7.gov.bc.ca`; -const retUrl = `https://${ZONE === "prod" ? "loginproxy" :ZONE === "test" ? "test.loginproxy": "dev.loginproxy"}.gov.bc.ca/auth/realms/standard/protocol/openid-connect/logout`; +const returnUrlHost = ZONE === "prod" ? "loginproxy" :ZONE === "test" ? "test.loginproxy": "dev.loginproxy"; +const retUrl = `https://${returnUrlHost}.gov.bc.ca/auth/realms/standard/protocol/openid-connect/logout`; const redirectSignOut = env.VITE_REDIRECT_SIGN_OUT && env.VITE_REDIRECT_SIGN_OUT.trim() !== "" diff --git a/frontend/src/components/ActionsTable/index.tsx b/frontend/src/components/ActionsTable/index.tsx index 30203d40..0dc84b85 100644 --- a/frontend/src/components/ActionsTable/index.tsx +++ b/frontend/src/components/ActionsTable/index.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { Table, TableHead, @@ -14,8 +13,8 @@ import { ActivityTagFileFormatEnum, ActivityTagTypeEnum } from '../../types/Acti import { ITableHeader } from '../../types/TableHeader'; interface IActionsTable { - rows: RecentAction[]; - headers: ITableHeader[]; + readonly rows: RecentAction[]; + readonly headers: ITableHeader[]; } /** @@ -68,8 +67,8 @@ function ActionsTable(props: IActionsTable): JSX.Element { - {props.rows.map((row: RecentAction, idx: number) => ( - + {props.rows.map((row: RecentAction) => ( + {headerKeys.map((key: string) => ( {key === "statusCode" ? ( diff --git a/frontend/src/components/BCHeader/index.tsx b/frontend/src/components/BCHeader/index.tsx index c54f62ad..36a68854 100644 --- a/frontend/src/components/BCHeader/index.tsx +++ b/frontend/src/components/BCHeader/index.tsx @@ -1,5 +1,4 @@ import React from "react"; -import { Link } from "react-router-dom"; import { useThemePreference } from "../../utils/ThemePreference"; import { toggleTheme } from "../../utils/ThemeFunction"; import { @@ -16,7 +15,7 @@ import { SideNavItems, HeaderSideNavItems, } from '@carbon/react'; -import { NavLink } from "react-router-dom"; +import { NavLink , Link} from "react-router-dom"; import * as Icons from '@carbon/icons-react'; import './BCHeader.scss' import { HeaderContainerProps } from "./definitions"; @@ -26,78 +25,76 @@ const BCHeader: React.FC = () => { const { theme, setTheme } = useThemePreference(); return ( - <> - ( -
- - - - SILVA - - - Link 1 - Link 2 - Link 3 - - Sub-link 1 - Sub-link 2 - Sub-link 3 - - - + ( +
+ + + + SILVA + + + Link 1 + Link 2 + Link 3 + + Sub-link 1 + Sub-link 2 + Sub-link 3 + + + - {toggleTheme(theme,setTheme)}} - > - {/* Must have a child component */} - <>{theme === 'g10'?:} - + {toggleTheme(theme,setTheme)}} + > + {/* Must have a child component */} + <>{theme === 'g10'?:} + - - - - - - + - + aria-label="Help" + > + + + + + + - - - - - Link 1 - Link 2 - Link 3 - - Sub-link 1 - Sub-link 2 - Sub-link 3 - - - - -
- )} - /> - +
+ + + + Link 1 + Link 2 + Link 3 + + Sub-link 1 + Sub-link 2 + Sub-link 3 + + + + +
+ )} + /> ); }; diff --git a/frontend/src/components/BCHeaderwSide/constants.ts b/frontend/src/components/BCHeaderwSide/constants.ts index 074342bc..98bac007 100644 --- a/frontend/src/components/BCHeaderwSide/constants.ts +++ b/frontend/src/components/BCHeaderwSide/constants.ts @@ -62,4 +62,4 @@ const managementItems: LeftMenu[] = [ export const leftMenu: LeftMenu[] = [ ...mainActivitiesItems, ...managementItems -]; +]; \ No newline at end of file diff --git a/frontend/src/components/BarChartGrouped/index.tsx b/frontend/src/components/BarChartGrouped/index.tsx index 2acd8ee9..c395c2f8 100644 --- a/frontend/src/components/BarChartGrouped/index.tsx +++ b/frontend/src/components/BarChartGrouped/index.tsx @@ -1,79 +1,62 @@ -import React, { useState, useEffect } from "react"; +// components/BarChartGrouped.tsx +import React, { useState } from "react"; 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 { useDistrictListQuery, useFetchOpeningsPerYear } from "../../services/queries/dashboard/dashboardQueries"; +import { IOpeningPerYear } from "../../types/IOpeningPerYear"; + import "@carbon/charts/styles.css"; import "./BarChartGrouped.scss"; -interface IDropdownItem { - value: string; - text: string; -} - -/** - * Renders an Bar Chart Grouped component. - * - * @returns {JSX.Element} The rendered BarChartGrouped component. - */ function 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 formatDateToString = (date: Date) => { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, "0"); + const day = String(date.getDate()).padStart(2, "0"); + return `${year}-${month}-${day}`; + }; + + const formattedStartDate = startDate ? formatDateToString(startDate) : null; + const formattedEndDate = endDate ? formatDateToString(endDate) : null; - const handleResize = () => { - setWindowWidth(window.innerWidth); + const queryProps: IOpeningPerYear = { + orgUnitCode, + statusCode, + entryDateStart: formattedStartDate, + entryDateEnd: formattedEndDate, }; - useEffect(() => { - const fetchChartData = async () => { - try { - setIsLoading(true); - let formattedStartDate: string | null = null; - let formattedEndDate: string | null = null; + // Fetch the openings submission trends data + const { data: chartData = [], isLoading } = useFetchOpeningsPerYear(queryProps); + // Fetch the org units (district list) data + const { data: orgunitsData = [], isLoading: isOrgUnitsLoading } = useDistrictListQuery(); - 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); - } catch (error) { - console.error("Error fetching chart data:", error); - setIsLoading(false); - } - }; + // Map the orgunitsData to create orgUnitItems for the Dropdown + const orgUnitItems = orgunitsData?.map((item: any) => ({ + text: item.orgUnitCode, + value: item.orgUnitCode, + })) || []; - fetchChartData(); - window.addEventListener("resize", handleResize); - return () => window.removeEventListener("resize", handleResize); - }, [orgUnitCode, statusCode, startDate, endDate]); + const statusItems = [ + { value: "APP", text: "Approved" }, + { value: "NAN", text: "Not Approved" }, + ]; - const formatDateToString = (dateToFormat: Date) => { - if (!dateToFormat) return null; - const year = dateToFormat.getFullYear(); - const month = String(dateToFormat.getMonth() + 1).padStart(2, "0"); - const day = String(dateToFormat.getDate()).padStart(2, "0"); - return `${year}-${month}-${day}`; + const setOrgUnitCodeSelected = ({ selectedItem }: { selectedItem: { value: string } }) => { + setOrgUnitCode(selectedItem.value); }; - const colors = { - Openings: "#1192E8", + const setStatusCodeSelected = ({ selectedItem }: { selectedItem: { value: string } }) => { + setStatusCode(selectedItem.value); }; + + const options = { axes: { left: { @@ -84,62 +67,13 @@ function BarChartGrouped(): JSX.Element { mapsTo: "key", }, }, - color: { - scale: colors, - }, + color: { scale: { Openings: "#1192E8" } }, height: "18.5rem", grid: { - x: { - enabled: false, - color: "#d3d3d3", - strokeDashArray: "2,2", - }, - y: { - enabled: true, - color: "#d3d3d3", - strokeDashArray: "2,2", - }, - }, - toolbar: { - enabled: false, - numberOfIcons: 2, - controls: [ - { - type: "Make fullscreen", - }, - { - type: "Make fullscreen", - }, - ], + x: { enabled: false, color: "#d3d3d3", strokeDashArray: "2,2" }, + y: { enabled: true, color: "#d3d3d3", strokeDashArray: "2,2" }, }, - }; - - 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 setStatusCodeSelected = ({ - selectedItem, - }: { - selectedItem: IDropdownItem; - }) => { - setStatusCode(selectedItem.value); + toolbar: { enabled: false }, }; return ( @@ -150,7 +84,7 @@ function BarChartGrouped(): JSX.Element { id="district-dropdown" titleText="District" items={orgUnitItems} - itemToString={(item: IDropdownItem) => (item ? item.text : "")} + itemToString={(item:any) => (item ? item.text : "")} onChange={setOrgUnitCodeSelected} label="District" /> @@ -160,35 +94,19 @@ function BarChartGrouped(): JSX.Element { id="status-dropdown" titleText="Status" items={statusItems} - itemToString={(item: IDropdownItem) => (item ? item.text : "")} + itemToString={(item:any) => (item ? item.text : "")} onChange={setStatusCodeSelected} label="Status" />
- setStartDate(dates[0])} - > - + setStartDate(dates[0])}> +
- setEndDate(dates[0])} - > - + setEndDate(dates[0])}> +
@@ -201,6 +119,6 @@ function BarChartGrouped(): JSX.Element { )} ); -}; +} export default BarChartGrouped; diff --git a/frontend/src/components/Dashboard/Opening/RecentOpeningsDataTable/index.tsx b/frontend/src/components/Dashboard/Opening/RecentOpeningsDataTable/index.tsx new file mode 100644 index 00000000..772a6a9c --- /dev/null +++ b/frontend/src/components/Dashboard/Opening/RecentOpeningsDataTable/index.tsx @@ -0,0 +1,331 @@ +import React, { useContext, useEffect, useState } from "react"; +import { + TableToolbar, + TableToolbarAction, + TableToolbarContent, + TableToolbarMenu, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableHeader, + TableRow, + Button, + Pagination, + OverflowMenu, + OverflowMenuItem, + Popover, + PopoverContent, + Checkbox, + CheckboxGroup, + Modal, + ActionableNotification +} from "@carbon/react"; +import * as Icons from "@carbon/icons-react"; +import StatusTag from "../../../StatusTag"; +import "./styles.scss"; +import EmptySection from "../../../EmptySection"; +import PaginationContext from "../../../../contexts/PaginationContext"; +import { OpeningsSearch } from "../../../../types/OpeningsSearch"; +import { ITableHeader } from "../../../../types/TableHeader"; +import { MenuItem } from "@carbon/react"; +import { + convertToCSV, + downloadCSV, + downloadPDF, + downloadXLSX, +} from "../../../../utils/fileConversions"; +import { Tooltip } from "@carbon/react"; +import { useNavigate } from "react-router-dom"; + +interface IRecentOpeningsDataTable { + rows: OpeningsSearch[]; + headers: ITableHeader[]; + defaultColumns: ITableHeader[]; + handleCheckboxChange: Function; + setOpeningId: Function; + toggleSpatial: Function; + showSpatial: boolean; + totalItems: number; +} + +const RecentOpeningsDataTable: React.FC = ({ + rows, + headers, + defaultColumns, + showSpatial, + totalItems, +}) => { + const { + handlePageChange, + handleItemsPerPageChange, + itemsPerPage, + setInitialItemsPerPage, + currentPage, + } = useContext(PaginationContext); + const alignTwo = document?.dir === "rtl" ? "bottom-left" : "bottom-right"; + const [openDownload, setOpenDownload] = useState(false); + const [selectedRows, setSelectedRows] = useState([]); // State to store selected rows + const [toastText, setToastText] = useState(null); + const [openingDetails, setOpeningDetails] = useState(false); + const navigate = useNavigate(); + + useEffect(() => { + setInitialItemsPerPage(itemsPerPage); + }, [rows, totalItems]); + + // Function to handle row selection changes + const handleRowSelectionChanged = (rowId: string) => { + setSelectedRows((prevSelectedRows) => { + if (prevSelectedRows.includes(rowId)) { + // If the row is already selected, remove it from the selected rows + return prevSelectedRows.filter((id) => id !== rowId); + } else { + // If the row is not selected, add it to the selected rows + return [...prevSelectedRows, rowId]; + } + }); + }; + + //Function to handle the favourite feature of the opening for a user + const handleFavouriteOpening = (rowId: string) => { + console.log(rowId + " has been added as a favourite for the user") + //make a call to the api for the favourite opening when ready + setToastText(`Following "OpeningID ${rowId}"`); + } + + return ( + <> + + + +
+

+ Total Search Results: {totalItems} +

+
+ + console.log("Download Click")}> + Print + + { + console.log("Clicked print"); + }} + > + Download + + +
+
+
+ setOpenDownload(false)} + > + + + { + downloadPDF(headers, rows); + }} + /> + { + const csvData = convertToCSV(headers, rows); + downloadCSV(csvData, "openings-data.csv"); + }} + /> + downloadXLSX(headers, rows)} + /> + + +
+
+
+ + + + {headers.map((header) => + header.selected ? ( + {header.header} + ) : null + )} + + + + {rows && + rows.map((row: any, i: number) => ( + { + //add the api call to send the viewed opening + // await handleRowClick(row.openingId); + setOpeningDetails(true); + }} + > + {headers.map((header) => + header.selected ? ( + + {header.key === "statusDescription" ? ( + + ) : header.key === "actions" ? ( + <> + <> +
+
+ + {rows.length <= 0 ? ( + + ) : null} + + {rows.length > 0 && ( + { + handlePageChange(page); + handleItemsPerPageChange(page, pageSize); + }} + /> + )} + {toastText != null ? ( + setToastText(null)} + actionButtonLabel="Go to track openings" + onActionButtonClick={() => + navigate("/opening?tab=metrics&scrollTo=trackOpenings") + } + /> + ) : null} + + setOpeningDetails(false)} + passiveModal + modalHeading="We are working hard to get this feature asap, unfortunately you cannot view the opening details from SILVA atm." + modalLabel="Opening Details" + /> + + ); +}; + +export default RecentOpeningsDataTable; diff --git a/frontend/src/components/Dashboard/Opening/RecentOpeningsDataTable/styles.scss b/frontend/src/components/Dashboard/Opening/RecentOpeningsDataTable/styles.scss new file mode 100644 index 00000000..badbfad9 --- /dev/null +++ b/frontend/src/components/Dashboard/Opening/RecentOpeningsDataTable/styles.scss @@ -0,0 +1,165 @@ +@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'; + + +.search-data-table{ + // nested elements for the search data table only + .table-toolbar{ + border-top: 1px solid var(--#{vars.$bcgov-prefix}-border-subtle-01); + } + .divider{ + width: 1px; + height: 48px; + background-color: var(--#{vars.$bcgov-prefix}-border-subtle-01); + } + .total-results-container{ + height: 100%; + align-items: center; + width: 100%; + display: flex; + padding: 0px 16px 0px 32px; + } + .total-search-results { + @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); + } + .bx--btn--ghost{ + min-height: 48px; + align-items: center; + } + + +} + + +.edit-column-content{ + width: 400px; + .dropdown-label { + padding:16px; + p { + @include type.type-style('label-02'); + font-size: 12px; + } + } + .dropdown-container{ + padding: 16px; + padding-top: 0px; + } + .menu-item{ + font-size: 12px; + } + .checkbox-item .bx--checkbox-label-text{ + font-size: 14px; + line-height: 18px; + letter-spacing: 0.16px; + font-weight: 400; + } + +} +.download-column-content{ + width: 240px; + .menu-item{ + padding: 16px; + } + +} + +.checkbox-tip span{ + @include type.type-style('body-compact-02'); + max-width: 205px; + font-size: 14px; + line-height: 18px; + letter-spacing: 0.16px; +} + +.fav-toast{ + position: fixed; + top: 64px; + right: 16px; + z-index:2; +} + +//Need to find selector for specific screen +.bx--overflow-menu-options{ + width:260px !important +} +.bx--overflow-menu-options__option-content { + overflow:visible; +} + +.activity-table { + margin-bottom: 2.5rem; + + tr > th:first-child, + tr > td:first-child { + padding-left: 2.5rem; + } + + tr > th:last-child, + tr > td:last-child { + padding-right: 2.5rem; + } + + tr > th:last-child div, + tr > td:last-child { + text-align: center; + } + + .activities-table-cell svg { + position: relative; + margin-right: 0.5rem; + top: 0.1875rem; + } +} + +.#{vars.$bcgov-prefix}--data-table thead tr th#blank { + min-width:50px; +} + +.#{vars.$bcgov-prefix}--data-table thead tr th { + background-color: #F3F3F5; + border-top: 1px solid; + border-color: var(--#{vars.$bcgov-prefix}-border-subtle-01); + background-color: var(--#{vars.$bcgov-prefix}-layer-accent-01) !important; +} + +.#{vars.$bcgov-prefix}--data-table thead tr th { + min-width:158px; +} + +.#{vars.$bcgov-prefix}--data-table tr:nth-child(even) td { + background-color: var(--#{vars.$bcgov-prefix}-layer-01) !important; + height: 64px; +} +.#{vars.$bcgov-prefix}--data-table tr:nth-child(odd) td { + background-color: var(--#{vars.$bcgov-prefix}-layer-02) !important; + height: 64px; +} +.#{vars.$bcgov-prefix}--data-table tr:hover td { + background-color: var(--#{vars.$bcgov-prefix}-layer-accent-02) !important; + cursor: pointer; +} +.#{vars.$bcgov-prefix}--pagination { + background-color: var(--#{vars.$bcgov-prefix}-layer-02) !important; +} + +.table-toolbar{ + background-color: var(--#{vars.$bcgov-prefix}-layer-02); + +} + +@media only screen and (max-width: 672px) { + .#{vars.$bcgov-prefix}--data-table-content { + width: 100%; + overflow-x: scroll; + } + + .activity-table { + width: 56.25rem; + } +} diff --git a/frontend/src/components/Dashboard/Opening/RecentOpeningsDataTable/testData.ts b/frontend/src/components/Dashboard/Opening/RecentOpeningsDataTable/testData.ts new file mode 100644 index 00000000..a3e5d814 --- /dev/null +++ b/frontend/src/components/Dashboard/Opening/RecentOpeningsDataTable/testData.ts @@ -0,0 +1,284 @@ +import { ITableHeader } from "../../../../types/TableHeader"; + +export const columns: ITableHeader[] = [ + { + key: 'openingId', + header: 'Opening Id', + selected: true + }, + { + key: 'forestFileId', + header: 'File Id', + selected: true + }, + { + key: 'cuttingPermitId', + header: 'Cutting permit', + selected: true + }, + { + key: 'timberMark', + header: 'Timber mark', + selected: true + }, + { + key: 'cutBlockId', + header: 'Cut block', + selected: true + }, + { + key: 'openingGrossAreaHa', + header: 'Gross Area', + selected: true + }, + + { + key: 'statusDescription', + header: 'Status', + selected: true + }, + { + key: 'categoryDescription', + header: 'Category', + selected: true + }, + { + key: 'disturbanceStartDate', + header: 'Disturbance Date', + selected: false + }, + { + key: 'actions', + header: 'Actions', + selected: true + } +]; + + +export const rows:any = [ + { + id: '114207', + openingId: '114207', + fileId: 'TFL47', + cuttingPermit: '12S', + timberMark: '47/12S', + cutBlock: '12-69', + grossAreaHa: '12.9', + status: 'Free growing', + category: 'FTML', + disturbanceStart: '-', + createdAt: '2022-10-27', + orgUnit: 'DCC - Cariboo chilcotin natural resources', + lastViewed: '2022-10-27' + }, + { + id: '114206', + openingId: '114206', + fileId: 'TFL47', + cuttingPermit: '12T', + timberMark: '47/12S', + cutBlock: '12-69', + grossAreaHa: '12.9', + status: 'Free growing', + category: 'FTML', + disturbanceStart: '-', + createdAt: '2022-09-04', + orgUnit: 'DCC - Cariboo chilcotin natural resources', + lastViewed: '2022-10-27' + }, + { + id: '114205', + openingId: '114205', + fileId: 'TFL47', + cuttingPermit: '12T', + timberMark: '47/12S', + cutBlock: '12-44A', + grossAreaHa: '12.9', + status: 'Free growing', + category: 'FTML', + disturbanceStart: '-', + createdAt: '2022-09-04', + orgUnit: 'DCC - Cariboo chilcotin natural resources', + lastViewed: '2022-10-27' + }, + { + id: '114204', + openingId: '114204', + fileId: 'TFL47', + cuttingPermit: '12T', + timberMark: '47/12S', + cutBlock: '12-44A', + grossAreaHa: '12.9', + status: 'Active', + category: 'FTML', + disturbanceStart: '-', + createdAt: '2022-01-16', + orgUnit: 'DCC - Cariboo chilcotin natural resources', + lastViewed: '2022-10-26' + }, + { + id: '114203', + openingId: '114203', + fileId: 'TFL47', + cuttingPermit: '12T', + timberMark: '47/12S', + cutBlock: '12-44A', + grossAreaHa: '12.9', + status: 'Active', + category: 'FTML', + disturbanceStart: '-', + createdAt: '2021-12-08', + orgUnit: 'DCC - Cariboo chilcotin natural resources', + lastViewed: '2022-10-26' + }, + { + id: '114202', + openingId: '114202', + fileId: 'TFL47', + cuttingPermit: '12T', + timberMark: '47/12S', + cutBlock: '12-44A', + grossAreaHa: '12.9', + status: 'Free growing', + category: 'FTML', + disturbanceStart: '-', + createdAt: '2021-11-15', + orgUnit: 'DCC - Cariboo chilcotin natural resources', + lastViewed: '2022-10-25' + }, + { + id: '114201', + openingId: '114201', + fileId: 'TFL47', + cuttingPermit: '12T', + timberMark: '47/12S', + cutBlock: '12-44A', + grossAreaHa: '12.9', + status: 'Free growing', + category: 'FTML', + disturbanceStart: '-', + createdAt: '2021-11-15', + orgUnit: 'DCC - Cariboo chilcotin natural resources', + lastViewed: '2022-10-25' + }, + { + id: '114200', + openingId: '114200', + fileId: 'TFL47', + cuttingPermit: '12T', + timberMark: '47/12S', + cutBlock: '12-44A', + grossAreaHa: '12.9', + status: 'Active', + category: 'FTML', + disturbanceStart: '-', + createdAt: '2021-10-20', + orgUnit: 'DCC - Cariboo chilcotin natural resources', + lastViewed: '2022-10-24' + }, + { + id: '114199', + openingId: '114199', + fileId: 'TFL47', + cuttingPermit: '12T', + timberMark: '47/12S', + cutBlock: '12-44A', + grossAreaHa: '12.9', + status: 'Active', + category: 'FTML', + disturbanceStart: '-', + createdAt: '2021-10-20', + orgUnit: 'DCC - Cariboo chilcotin natural resources', + lastViewed: '2022-10-24' + }, + { + id: '114198', + openingId: '114198', + fileId: 'TFL47', + cuttingPermit: '12T', + timberMark: '47/12S', + cutBlock: '12-44A', + grossAreaHa: '12.9', + status: 'Free growing', + category: 'FTML', + disturbanceStart: '-', + createdAt: '2021-09-12', + orgUnit: 'DCC - Cariboo chilcotin natural resources', + lastViewed: '2022-10-23' + }, + { + id: '114197', + openingId: '114197', + fileId: 'TFL47', + cuttingPermit: '12T', + timberMark: '47/12S', + cutBlock: '12-44A', + grossAreaHa: '12.9', + status: 'Free growing', + category: 'FTML', + disturbanceStart: '-', + createdAt: '2021-09-12', + orgUnit: 'DCC - Cariboo chilcotin natural resources', + lastViewed: '2022-10-23' + }, + { + id: '114196', + openingId: '114196', + fileId: 'TFL47', + cuttingPermit: '12T', + timberMark: '47/12S', + cutBlock: '12-44A', + grossAreaHa: '12.9', + status: 'Free growing', + category: 'FTML', + disturbanceStart: '-', + createdAt: '2021-08-05', + orgUnit: 'DCC - Cariboo chilcotin natural resources', + lastViewed: '2022-10-22' + }, + { + id: '114195', + openingId: '114195', + fileId: 'TFL47', + cuttingPermit: '12T', + timberMark: '47/12S', + cutBlock: '12-44A', + grossAreaHa: '12.9', + status: 'Free growing', + category: 'FTML', + disturbanceStart: '-', + createdAt: '2021-08-05', + orgUnit: 'DCC - Cariboo chilcotin natural resources', + lastViewed: '2022-10-22' + }, + { + id: '114194', + openingId: '114194', + fileId: 'TFL47', + cuttingPermit: '12T', + timberMark: '47/12S', + cutBlock: '12-44A', + grossAreaHa: '12.9', + status: 'Active', + category: 'FTML', + disturbanceStart: '-', + createdAt: '2021-07-10', + orgUnit: 'DCC - Cariboo chilcotin natural resources', + lastViewed: '2022-10-21' + }, + { + id: '114193', + openingId: '114193', + fileId: 'TFL47', + cuttingPermit: '12T', + timberMark: '47/12S', + cutBlock: '12-44A', + grossAreaHa: '12.9', + status: 'Active', + category: 'FTML', + disturbanceStart: '-', + createdAt: '2021-07-10', + orgUnit: 'DCC - Cariboo chilcotin natural resources', + lastViewed: '2022-10-21' + } +]; diff --git a/frontend/src/components/DoughnutChartView/index.tsx b/frontend/src/components/DoughnutChartView/index.tsx index 07f52861..0855b351 100644 --- a/frontend/src/components/DoughnutChartView/index.tsx +++ b/frontend/src/components/DoughnutChartView/index.tsx @@ -2,7 +2,8 @@ import React, { useState, useEffect, ChangeEvent, useCallback } from "react"; import { DonutChart } from "@carbon/charts-react"; import { Dropdown, DatePicker, DatePickerInput, TextInput } from "@carbon/react"; import "./DoughnutChartView.scss"; -import { IFreeGrowingChartData, fetchFreeGrowingMilestones } from "../../services/OpeningService"; +import { fetchFreeGrowingMilestones } from "../../services/OpeningService"; +import { IFreeGrowingChartData } from "../../types/OpeningTypes"; interface IDropdownItem { value: string, diff --git a/frontend/src/components/FavoriteButton/index.tsx b/frontend/src/components/FavoriteButton/index.tsx index 9d1c7513..8d8cb11a 100644 --- a/frontend/src/components/FavoriteButton/index.tsx +++ b/frontend/src/components/FavoriteButton/index.tsx @@ -8,6 +8,8 @@ interface FavoriteButtonProps { kind: string; size: string; fill: string; + favorited: boolean; + onFavoriteChange: (newStatus: boolean) => void; } /** @@ -18,6 +20,7 @@ interface FavoriteButtonProps { * @param {string} props.kind - The favourite button kind. * @param {string} props.size - The favourite button size. * @param {string} props.fill - The favourite button fill. + * @param {boolean} props.favorited - The favourite button state. * @returns {JSX.Element} The FavoriteButton element to be rendered. */ function FavoriteButton({ @@ -25,11 +28,14 @@ function FavoriteButton({ kind, size, fill, + favorited = false, + onFavoriteChange }: FavoriteButtonProps): JSX.Element { - const [isFavorite, setIsFavorite] = useState(false); + const [isFavorite, setIsFavorite] = useState(favorited); const handleClick = () => { setIsFavorite(!isFavorite); + onFavoriteChange(!isFavorite); }; const iconName = isFavorite ? 'FavoriteFilled' : 'Favorite'; @@ -39,7 +45,6 @@ function FavoriteButton({ if (!Icon) { return
Invalid icon name
; } - const CustomIcon = () => ; return ( diff --git a/frontend/src/components/OpeningHistory/index.tsx b/frontend/src/components/OpeningHistory/index.tsx index 6dc6d22e..936968d4 100644 --- a/frontend/src/components/OpeningHistory/index.tsx +++ b/frontend/src/components/OpeningHistory/index.tsx @@ -1,5 +1,4 @@ -import React from 'react'; - +import React, { useState, useEffect } from "react"; import { ProgressIndicator, ProgressStep @@ -9,25 +8,63 @@ import History from '../../types/History'; import statusClass from '../../utils/HistoryStatus'; import FavoriteButton from '../FavoriteButton'; import './styles.scss'; +import { useNotification } from '../../contexts/NotificationProvider'; +import { fetchOpeningTrends, deleteOpeningFavorite } from "../../services/OpeningFavoriteService"; + + +const OpeningHistory: React.FC = () => { + const { displayNotification } = useNotification(); + const [histories, setHistories] = useState([]); + + const loadTrends = async () => { + const history = await fetchOpeningTrends(); + setHistories(history?.map(item => ({ id: item, steps: [] })) || []); + }; + + useEffect(() => { loadTrends(); },[]); + + + const handleFavoriteChange = async (newStatus: boolean, openingId: number) => { + try { + if(!newStatus){ + await deleteOpeningFavorite(openingId); + displayNotification({ + title: 'Favorite Removed', + subTitle: `Opening Id ${openingId} removed from favorites`, + type: 'success', + dismissIn: 8000, + onClose: () => {} + }); + loadTrends(); + } + } catch (error) { + displayNotification({ + title: 'Error', + subTitle: `Failed to update favorite status for ${openingId}`, + type: 'error', + dismissIn: 8000, + onClose: () => {} + }); + } + }; -interface OpeningHistoryProps { - histories: History[]; -} + return ( -const OpeningHistory = ({ histories }: OpeningHistoryProps) => (
{histories.map((history, index) => (
-
+
handleFavoriteChange(newStatus, history.id)} />
{`Opening Id ${history.id}`} @@ -56,5 +93,6 @@ const OpeningHistory = ({ histories }: OpeningHistoryProps) => (
); +}; export default OpeningHistory; diff --git a/frontend/src/components/OpeningMetricsTab/index.tsx b/frontend/src/components/OpeningMetricsTab/index.tsx index 49203529..7325af4b 100644 --- a/frontend/src/components/OpeningMetricsTab/index.tsx +++ b/frontend/src/components/OpeningMetricsTab/index.tsx @@ -5,20 +5,21 @@ import BarChartGrouped from "../BarChartGrouped"; import ChartContainer from "../ChartContainer"; import DoughnutChartView from "../DoughnutChartView"; import OpeningHistory from "../OpeningHistory"; -import OpeningHistoryItems from "../../mock-data/OpeningHistoryItems"; import MyRecentActions from "../MyRecentActions"; const OpeningMetricsTab: React.FC = () => { const trackOpeningRef = useRef(null); - + // Optional: Scroll to "Track Openings" when this component mounts useEffect(() => { + const params = new URLSearchParams(window.location.search); const scrollToSection = params.get('scrollTo'); if (scrollToSection === 'trackOpenings' && trackOpeningRef.current) { trackOpeningRef.current.scrollIntoView({ behavior: "smooth" }); } + }, []); return ( @@ -37,10 +38,8 @@ const OpeningMetricsTab: React.FC = () => {
{/* Add ref here to scroll */} - - + +
diff --git a/frontend/src/components/OpeningsTab/index.tsx b/frontend/src/components/OpeningsTab/index.tsx index 26f3a8f4..9d51227e 100644 --- a/frontend/src/components/OpeningsTab/index.tsx +++ b/frontend/src/components/OpeningsTab/index.tsx @@ -4,8 +4,7 @@ import './styles.scss' import { Location } from '@carbon/icons-react'; import OpeningsMap from '../OpeningsMap'; import OpeningScreenDataTable from '../OpeningScreenDataTable/index'; -import { headers } from '../OpeningScreenDataTable/testData'; -import { fetchRecentOpenings } from '../../services/OpeningService'; +import { columns } from '../Dashboard/Opening/RecentOpeningsDataTable/testData'; import SectionTitle from '../SectionTitle'; import TableSkeleton from '../TableSkeleton'; import { InlineNotification } from '@carbon/react'; @@ -14,6 +13,9 @@ import { useSelector } from 'react-redux'; import { RootState } from '../../store'; import { generateHtmlFile } from './layersGenerator'; import { getWmsLayersWhitelistUsers, WmsLayersWhitelistUser } from '../../services/SecretsService'; +import { useUserRecentOpeningQuery } from '../../services/queries/search/openingQueries'; +import RecentOpeningsDataTable from '../Dashboard/Opening/RecentOpeningsDataTable'; +import { ITableHeader } from '../../types/TableHeader'; interface Props { showSpatial: boolean; @@ -21,40 +23,16 @@ interface Props { } const OpeningsTab: React.FC = ({ showSpatial, setShowSpatial }) => { - const [loading, setLoading] = useState(true); const [openingRows, setOpeningRows] = useState([]); const [error, setError] = useState(null); const [loadId, setLoadId] = useState(null); const [openingPolygonNotFound, setOpeningPolygonNotFound] = useState(false); const [wmsUsersWhitelist, setWmsUsersWhitelist] = useState([]); const userDetails = useSelector((state: RootState) => state.userDetails); + const { data, isFetching } = useUserRecentOpeningQuery(10); + const [headers, setHeaders] = useState(columns); - useEffect(() => { - const fetchData = async () => { - try { - const rows: RecentOpening[] = await fetchRecentOpenings(); - setOpeningRows(rows); - setLoading(false); - setError(null); - } catch (error) { - console.error('Error fetching recent openings:', error); - setLoading(false); - setError('Failed to fetch recent openings'); - } - }; - - const fetchAllowedPeople = async () => { - try { - const usersList: WmsLayersWhitelistUser[] = await getWmsLayersWhitelistUsers(); - setWmsUsersWhitelist(usersList); - } catch (error) { - console.error('Error fetching recent openings:', error); - } - }; - - fetchData(); - fetchAllowedPeople(); - }, []); + useEffect(() => {}, [loadId, openingPolygonNotFound, wmsUsersWhitelist]); @@ -73,6 +51,31 @@ const OpeningsTab: React.FC = ({ showSpatial, setShowSpatial }) => { } }; + const handleCheckboxChange = (columnKey: string) => { + if(columnKey === "select-default"){ + //set to the deafult + setHeaders(columns) + } + else if(columnKey === "select-all"){ + setHeaders((prevHeaders) => + prevHeaders.map((header) => ({ + ...header, + selected: true, // Select all headers + })) + ); + } + else{ + setHeaders((prevHeaders) => + prevHeaders.map((header) => + header.key === columnKey + ? { ...header, selected: !header.selected } + : header + ) + ); + } + + }; + return ( <>
@@ -113,19 +116,23 @@ const OpeningsTab: React.FC = ({ showSpatial, setShowSpatial }) => { className = "inline-notification" /> ) : null } - {loading ? ( + {isFetching ? ( ) : ( - + )}
); }; -export default OpeningsTab; +export default OpeningsTab; \ No newline at end of file diff --git a/frontend/src/components/SectionTitle/index.tsx b/frontend/src/components/SectionTitle/index.tsx index c1ea337e..bf394623 100644 --- a/frontend/src/components/SectionTitle/index.tsx +++ b/frontend/src/components/SectionTitle/index.tsx @@ -1,13 +1,5 @@ -import React from 'react'; - -import { - Column, - IconButton -} from '@carbon/react'; -import { Favorite, FavoriteFilled } from '@carbon/icons-react'; - +import { Column } from '@carbon/react'; import Subtitle from '../Subtitle'; - import './styles.scss'; interface SectionTitleProps { @@ -15,13 +7,11 @@ interface SectionTitleProps { subtitle: string; enableFavourite?: boolean; activity?: string; - onClick?: () => void; } const SectionTitle = ({ title, - subtitle, - onClick + subtitle }: SectionTitleProps) => { return ( @@ -29,7 +19,7 @@ const SectionTitle = ({

{title}

- + ); }; diff --git a/frontend/src/components/SilvicultureSearch/Openings/AdvancedSearchDropdown/AdvancedSearchDropdown.scss b/frontend/src/components/SilvicultureSearch/Openings/AdvancedSearchDropdown/AdvancedSearchDropdown.scss index 5f40c7c6..15e8ef86 100644 --- a/frontend/src/components/SilvicultureSearch/Openings/AdvancedSearchDropdown/AdvancedSearchDropdown.scss +++ b/frontend/src/components/SilvicultureSearch/Openings/AdvancedSearchDropdown/AdvancedSearchDropdown.scss @@ -15,6 +15,10 @@ background-color: var(--bx-field-01); border-bottom: 1px solid var(--bx-border-strong-01); } + .multi-select .bx--list-box__field--wrapper{ + background-color: var(--bx-field-01); + border-bottom: 1px solid var(--bx-border-strong-01); + } } diff --git a/frontend/src/components/SilvicultureSearch/Openings/AdvancedSearchDropdown/index.tsx b/frontend/src/components/SilvicultureSearch/Openings/AdvancedSearchDropdown/index.tsx index 3821b73e..fd37f902 100644 --- a/frontend/src/components/SilvicultureSearch/Openings/AdvancedSearchDropdown/index.tsx +++ b/frontend/src/components/SilvicultureSearch/Openings/AdvancedSearchDropdown/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from "react"; +import React, { useState, useEffect } from "react"; import { Checkbox, CheckboxGroup, @@ -12,27 +12,63 @@ import { FlexGrid, Row, Column, + FilterableMultiSelect } from "@carbon/react"; import "./AdvancedSearchDropdown.scss"; import * as Icons from "@carbon/icons-react"; import { useOpeningFiltersQuery } from "../../../../services/queries/search/openingQueries"; import { useOpeningsSearch } from "../../../../contexts/search/OpeningsSearch"; -import { color } from "@carbon/charts"; interface AdvancedSearchDropdownProps { toggleShowFilters: () => void; // Function to be passed as a prop } -const AdvancedSearchDropdown: React.FC = ({ -}) => { - const { filters, setFilters, clearFilters } = useOpeningsSearch(); +const AdvancedSearchDropdown: React.FC = () => { + const { filters, setFilters } = useOpeningsSearch(); const { data, isLoading, isError } = useOpeningFiltersQuery(); + // Initialize selected items for OrgUnit MultiSelect based on existing filters + const [selectedOrgUnits, setSelectedOrgUnits] = useState([]); + // Initialize selected items for category MultiSelect based on existing filters + const [selectedCategories, setSelectedCategories] = useState([]); + + useEffect(() => { + // Split filters.orgUnit into array and format as needed for selectedItems + if (filters.orgUnit) { + const orgUnitsArray = filters.orgUnit.map((orgUnit: string) => ({ + text: orgUnit, + value: orgUnit, + })); + setSelectedOrgUnits(orgUnitsArray); + } else { + setSelectedOrgUnits([]); + } + // Split filters.category into array and format as needed for selectedItems + if (filters.category) { + const categoriesArray = filters.category.map((category: string) => ({ + text: category, + value: category, + })); + setSelectedCategories(categoriesArray); + } else{ + setSelectedCategories([]); + } + }, [filters.orgUnit, filters.category]); + const handleFilterChange = (updatedFilters: Partial) => { const newFilters = { ...filters, ...updatedFilters }; setFilters(newFilters); }; + const handleMultiSelectChange = (group: string, selectedItems: any) => { + const updatedGroup = selectedItems.map((item: any) => item.value); + if (group === "orgUnit") + setSelectedOrgUnits(updatedGroup); + if (group === "category") + setSelectedCategories(updatedGroup); + handleFilterChange({ [group]: updatedGroup }); + } + const handleCheckboxChange = (value: string, group: string) => { const selectedGroup = filters[group as keyof typeof filters] as string[]; const updatedGroup = selectedGroup.includes(value) @@ -72,12 +108,6 @@ const AdvancedSearchDropdown: React.FC = ({ value: item.value, })) || []; - const blockStatusItems = - data.blockStatuses?.map((item: any) => ({ - text: item.label, - value: item.value, - })) || []; - return (
@@ -119,41 +149,29 @@ const AdvancedSearchDropdown: React.FC = ({ - (item ? item.text : "")} - onChange={(e: any) => - handleFilterChange({ orgUnit: e.selectedItem.value }) - } + item.value === filters.orgUnit - ) - : "" - } + id="orgunit-multiselect" + className="multi-select" + titleText="Org Unit" + items={orgUnitItems} + itemToString={(item: any) => (item ? item.value : "")} + selectionFeedback="top-after-reopen" + onChange={(e: any) => handleMultiSelectChange("orgUnit", e.selectedItems)} + selectedItems={selectedOrgUnits} /> - (item ? item.text : "")} - onChange={(e: any) => - handleFilterChange({ category: e.selectedItem.value }) - } - label="Enter or choose a category" - selectedItem={ - filters.category - ? categoryItems.find( - (item: any) => item.value === filters.category - ) - : "" - } + itemToString={(item: any) => (item ? item.value : "")} + selectionFeedback="top-after-reopen" + onChange={(e: any) => handleMultiSelectChange("category", e.selectedItems)} + selectedItems={selectedCategories} /> @@ -168,7 +186,7 @@ const AdvancedSearchDropdown: React.FC = ({ label="If you don't remember the client information you can go to client search." > = ({ -
= ({ - onSearchClick, + onSearchClick }) => { const [isOpen, setIsOpen] = useState(false); const [showFilters, setShowFilters] = useState(false); @@ -30,14 +30,20 @@ const OpeningsSearchBar: React.FC = ({ }; const handleSearchClick = () => { + //set the Advanced Filter Dropsdown visibility to false + setIsOpen(false); onSearchClick(); }; + // this function calls handleSearchClick when the enter key is pressed + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === "Enter") { + handleSearchClick(); + } + }; const handleInputChange = (e: React.ChangeEvent) => { const value = e.target.value; setSearchTerm(value); - // setSearchInput(value); - // onSearchInputChange(value); // Call the function to update the search input in the parent component }; const handleFiltersChanged = () => { @@ -62,7 +68,7 @@ const OpeningsSearchBar: React.FC = ({ closeButtonLabelText="Clear search input" id={`search-1`} onChange={handleInputChange} // Handle input change - onKeyDown={() => {}} + onKeyDown={handleKeyDown} // Handle enter key press value={searchTerm} /> @@ -70,15 +76,13 @@ const OpeningsSearchBar: React.FC = ({
{filtersCount > 0 ? ( - clearFilters()} > - {"+" + filtersCount} - + ) : null}

0 ? "text-active" : ""}> Advanced Search diff --git a/frontend/src/components/SilvicultureSearch/Openings/OpeningsSearchTab/index.tsx b/frontend/src/components/SilvicultureSearch/Openings/OpeningsSearchTab/index.tsx index 14f9ea33..a73947da 100644 --- a/frontend/src/components/SilvicultureSearch/Openings/OpeningsSearchTab/index.tsx +++ b/frontend/src/components/SilvicultureSearch/Openings/OpeningsSearchTab/index.tsx @@ -13,15 +13,13 @@ import { ITableHeader } from "../../../../types/TableHeader"; import { countActiveFilters } from "../../../../utils/searchUtils"; const OpeningsSearchTab: React.FC = () => { - const [showSpatial, setShowSpatial] = useState(false); - const [loadId, setLoadId] = useState(null); - const [openingPolygonNotFound, setOpeningPolygonNotFound] = - useState(false); + const [showSpatial, setShowSpatial] = useState(false); + const [openingPolygonNotFound, setOpeningPolygonNotFound] = useState(false); const [filtersApplied, setFiltersApplied] = useState(false); const [searchParams, setSearchParams] = useState>({}); const [finalParams, setFinalParams] = useState>({}); // Store params for query after search const [isSearchTriggered, setIsSearchTriggered] = useState(false); // Trigger state for search - const { currentPage, itemsPerPage, totalResultItems } = useContext(PaginationContext); + const { currentPage, itemsPerPage } = useContext(PaginationContext); const [headers, setHeaders] = useState(columns); @@ -114,11 +112,6 @@ const OpeningsSearchTab: React.FC = () => { } },[]) - // useEffect(()=>{ - // console.log("new filters") - // console.log(filters) - // },[filters]) - return ( <>

@@ -145,8 +138,7 @@ const OpeningsSearchTab: React.FC = () => { rows={data?.data || []} headers={headers} defaultColumns={columns} - handleCheckboxChange={handleCheckboxChange} - setOpeningId={setLoadId} + handleCheckboxChange={handleCheckboxChange} toggleSpatial={toggleSpatial} showSpatial={showSpatial} totalItems={(data?.perPage ?? 0) * (data?.totalPages ?? 0)} diff --git a/frontend/src/components/SilvicultureSearch/Openings/SearchScreenDataTable/index.tsx b/frontend/src/components/SilvicultureSearch/Openings/SearchScreenDataTable/index.tsx index fd3b43c3..f638870f 100644 --- a/frontend/src/components/SilvicultureSearch/Openings/SearchScreenDataTable/index.tsx +++ b/frontend/src/components/SilvicultureSearch/Openings/SearchScreenDataTable/index.tsx @@ -22,7 +22,7 @@ import { Row, Column, MenuItemDivider, - ToastNotification, + Modal, ActionableNotification } from "@carbon/react"; import * as Icons from "@carbon/icons-react"; @@ -42,6 +42,7 @@ import { } from "../../../../utils/fileConversions"; import { Tooltip } from "@carbon/react"; import { useNavigate } from "react-router-dom"; +import { usePostViewedOpening } from "../../../../services/queries/dashboard/dashboardQueries"; interface ISearchScreenDataTable { rows: OpeningsSearch[]; @@ -76,6 +77,8 @@ const SearchScreenDataTable: React.FC = ({ const [openDownload, setOpenDownload] = useState(false); const [selectedRows, setSelectedRows] = useState([]); // State to store selected rows const [toastText, setToastText] = useState(null); + const [openingDetails, setOpeningDetails] = useState(false); + const { mutate: markAsViewedOpening, isError, error } = usePostViewedOpening(); const navigate = useNavigate(); useEffect(() => { @@ -95,6 +98,20 @@ const SearchScreenDataTable: React.FC = ({ }); }; + const handleRowClick = (openingId: string) => { + // Call the mutation to mark as viewed + markAsViewedOpening(openingId, { + onSuccess: () => { + // setToastText(`Successfully marked opening ${openingId} as viewed.`); + console.log(`Successfully marked opening ${openingId} as viewed.`); + }, + onError: (err: any) => { + // setToastText(`Failed to mark as viewed: ${err.message}`); + console.log(`Failed to mark as viewed: ${err.message}`); + } + }); + }; + //Function to handle the favourite feature of the opening for a user const handleFavouriteOpening = (rowId: string) => { console.log(rowId + " has been added as a favourite for the user") @@ -272,7 +289,15 @@ const SearchScreenDataTable: React.FC = ({ {rows && rows.map((row: any, i: number) => ( - + { + //add the api call to send the viewed opening + await handleRowClick(row.openingId); + setOpeningDetails(true) + } + } + > {headers.map((header) => header.selected ? ( = ({
)} - + e.stopPropagation()} // Stop row onClick from triggering + > - handleFavouriteOpening(row.openingId) - } + onClick={(e: any) => { + e.stopPropagation(); // Stop row onClick from triggering + handleFavouriteOpening(row.openingId); + }} /> - downloadPDF(defaultColumns, [row]) - } + onClick={(e: any) => { + e.stopPropagation(); // Stop row onClick from triggering + downloadPDF(defaultColumns, [row]); + }} /> { + onClick={(e: any) => { + e.stopPropagation(); // Stop row onClick from triggering const csvData = convertToCSV(defaultColumns, [ row, ]); @@ -382,7 +414,7 @@ const SearchScreenDataTable: React.FC = ({ }} /> )} - {toastText!=null ? ( + {toastText != null ? ( = ({ closeOnEscape onClose={() => setToastText(null)} actionButtonLabel="Go to track openings" - onActionButtonClick = {() => navigate('/opening?tab=metrics&scrollTo=trackOpenings')} - + onActionButtonClick={() => + navigate("/opening?tab=metrics&scrollTo=trackOpenings") + } /> ) : null} + + setOpeningDetails(false)} + passiveModal + modalHeading="We are working hard to get this feature asap, unfortunately you cannot view the opening details from SILVA atm." + modalLabel="Opening Details" + /> ); }; diff --git a/frontend/src/components/SilvicultureSearch/Openings/SearchScreenDataTable/testData.ts b/frontend/src/components/SilvicultureSearch/Openings/SearchScreenDataTable/testData.ts index f8c6d7ba..35c29760 100644 --- a/frontend/src/components/SilvicultureSearch/Openings/SearchScreenDataTable/testData.ts +++ b/frontend/src/components/SilvicultureSearch/Openings/SearchScreenDataTable/testData.ts @@ -52,232 +52,3 @@ export const columns: ITableHeader[] = [ selected: false } ]; - - -export const rows:any = [ - { - id: '114207', - openingId: '114207', - fileId: 'TFL47', - cuttingPermit: '12S', - timberMark: '47/12S', - cutBlock: '12-69', - grossAreaHa: '12.9', - status: 'Free growing', - category: 'FTML', - disturbanceStart: '-', - createdAt: '2022-10-27', - orgUnit: 'DCC - Cariboo chilcotin natural resources', - lastViewed: '2022-10-27' - }, - { - id: '114206', - openingId: '114206', - fileId: 'TFL47', - cuttingPermit: '12T', - timberMark: '47/12S', - cutBlock: '12-69', - grossAreaHa: '12.9', - status: 'Free growing', - category: 'FTML', - disturbanceStart: '-', - createdAt: '2022-09-04', - orgUnit: 'DCC - Cariboo chilcotin natural resources', - lastViewed: '2022-10-27' - }, - { - id: '114205', - openingId: '114205', - fileId: 'TFL47', - cuttingPermit: '12T', - timberMark: '47/12S', - cutBlock: '12-44A', - grossAreaHa: '12.9', - status: 'Free growing', - category: 'FTML', - disturbanceStart: '-', - createdAt: '2022-09-04', - orgUnit: 'DCC - Cariboo chilcotin natural resources', - lastViewed: '2022-10-27' - }, - { - id: '114204', - openingId: '114204', - fileId: 'TFL47', - cuttingPermit: '12T', - timberMark: '47/12S', - cutBlock: '12-44A', - grossAreaHa: '12.9', - status: 'Active', - category: 'FTML', - disturbanceStart: '-', - createdAt: '2022-01-16', - orgUnit: 'DCC - Cariboo chilcotin natural resources', - lastViewed: '2022-10-26' - }, - { - id: '114203', - openingId: '114203', - fileId: 'TFL47', - cuttingPermit: '12T', - timberMark: '47/12S', - cutBlock: '12-44A', - grossAreaHa: '12.9', - status: 'Active', - category: 'FTML', - disturbanceStart: '-', - createdAt: '2021-12-08', - orgUnit: 'DCC - Cariboo chilcotin natural resources', - lastViewed: '2022-10-26' - }, - { - id: '114202', - openingId: '114202', - fileId: 'TFL47', - cuttingPermit: '12T', - timberMark: '47/12S', - cutBlock: '12-44A', - grossAreaHa: '12.9', - status: 'Free growing', - category: 'FTML', - disturbanceStart: '-', - createdAt: '2021-11-15', - orgUnit: 'DCC - Cariboo chilcotin natural resources', - lastViewed: '2022-10-25' - }, - { - id: '114201', - openingId: '114201', - fileId: 'TFL47', - cuttingPermit: '12T', - timberMark: '47/12S', - cutBlock: '12-44A', - grossAreaHa: '12.9', - status: 'Free growing', - category: 'FTML', - disturbanceStart: '-', - createdAt: '2021-11-15', - orgUnit: 'DCC - Cariboo chilcotin natural resources', - lastViewed: '2022-10-25' - }, - { - id: '114200', - openingId: '114200', - fileId: 'TFL47', - cuttingPermit: '12T', - timberMark: '47/12S', - cutBlock: '12-44A', - grossAreaHa: '12.9', - status: 'Active', - category: 'FTML', - disturbanceStart: '-', - createdAt: '2021-10-20', - orgUnit: 'DCC - Cariboo chilcotin natural resources', - lastViewed: '2022-10-24' - }, - { - id: '114199', - openingId: '114199', - fileId: 'TFL47', - cuttingPermit: '12T', - timberMark: '47/12S', - cutBlock: '12-44A', - grossAreaHa: '12.9', - status: 'Active', - category: 'FTML', - disturbanceStart: '-', - createdAt: '2021-10-20', - orgUnit: 'DCC - Cariboo chilcotin natural resources', - lastViewed: '2022-10-24' - }, - { - id: '114198', - openingId: '114198', - fileId: 'TFL47', - cuttingPermit: '12T', - timberMark: '47/12S', - cutBlock: '12-44A', - grossAreaHa: '12.9', - status: 'Free growing', - category: 'FTML', - disturbanceStart: '-', - createdAt: '2021-09-12', - orgUnit: 'DCC - Cariboo chilcotin natural resources', - lastViewed: '2022-10-23' - }, - { - id: '114197', - openingId: '114197', - fileId: 'TFL47', - cuttingPermit: '12T', - timberMark: '47/12S', - cutBlock: '12-44A', - grossAreaHa: '12.9', - status: 'Free growing', - category: 'FTML', - disturbanceStart: '-', - createdAt: '2021-09-12', - orgUnit: 'DCC - Cariboo chilcotin natural resources', - lastViewed: '2022-10-23' - }, - { - id: '114196', - openingId: '114196', - fileId: 'TFL47', - cuttingPermit: '12T', - timberMark: '47/12S', - cutBlock: '12-44A', - grossAreaHa: '12.9', - status: 'Free growing', - category: 'FTML', - disturbanceStart: '-', - createdAt: '2021-08-05', - orgUnit: 'DCC - Cariboo chilcotin natural resources', - lastViewed: '2022-10-22' - }, - { - id: '114195', - openingId: '114195', - fileId: 'TFL47', - cuttingPermit: '12T', - timberMark: '47/12S', - cutBlock: '12-44A', - grossAreaHa: '12.9', - status: 'Free growing', - category: 'FTML', - disturbanceStart: '-', - createdAt: '2021-08-05', - orgUnit: 'DCC - Cariboo chilcotin natural resources', - lastViewed: '2022-10-22' - }, - { - id: '114194', - openingId: '114194', - fileId: 'TFL47', - cuttingPermit: '12T', - timberMark: '47/12S', - cutBlock: '12-44A', - grossAreaHa: '12.9', - status: 'Active', - category: 'FTML', - disturbanceStart: '-', - createdAt: '2021-07-10', - orgUnit: 'DCC - Cariboo chilcotin natural resources', - lastViewed: '2022-10-21' - }, - { - id: '114193', - openingId: '114193', - fileId: 'TFL47', - cuttingPermit: '12T', - timberMark: '47/12S', - cutBlock: '12-44A', - grossAreaHa: '12.9', - status: 'Active', - category: 'FTML', - disturbanceStart: '-', - createdAt: '2021-07-10', - orgUnit: 'DCC - Cariboo chilcotin natural resources', - lastViewed: '2022-10-21' - } -]; diff --git a/frontend/src/components/StandardCard/index.tsx b/frontend/src/components/StandardCard/index.tsx deleted file mode 100644 index bc3ef137..00000000 --- a/frontend/src/components/StandardCard/index.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; - -import { useNavigate } from 'react-router-dom'; - -import { Tile, IconButton } from '@carbon/react'; -import * as Icons from '@carbon/icons-react'; -import * as Pictograms from '@carbon/pictograms-react'; - -import './style.scss'; - -interface StandardCardProps { - header: string; - description: string; - url: string; - image: string; -} - -const StandardCard = ({ - header, description, url, image -}: StandardCardProps) => { - const navigate = useNavigate(); - const Image = Pictograms[image]; - return ( - navigate(url)}> -
-
-

{header}

-
-

{description}

-
- - -
-
-
- ); -}; - -export default StandardCard; \ No newline at end of file diff --git a/frontend/src/components/StandardCard/style.scss b/frontend/src/components/StandardCard/style.scss deleted file mode 100644 index 931f9d3c..00000000 --- a/frontend/src/components/StandardCard/style.scss +++ /dev/null @@ -1,70 +0,0 @@ -@use '@bcgov-nr/nr-theme/design-tokens/variables.scss' as vars; -@use '@carbon/type'; - -.std-card-header { - display: inline-flex; - justify-content: space-between; - margin-bottom: 0.25rem; -} - -.std-card-title { - width: 15rem; -} - -p.std-card-title { - @include type.type-style('heading-03'); -} - -.std-card-main { - box-sizing: border-box; - display: flex; - flex-direction: column; - padding: 1.25rem 1rem; - margin: 0.5rem 2rem 0.5rem 0; - width: 20.25rem; - height: 22.5625rem; - background: var(--#{vars.$bcgov-prefix}-layer-02); - border: 1px solid var(--#{vars.$bcgov-prefix}-border-subtle-02); - border-radius: 4px; - flex: none; - order: 0; - flex-grow: 0; -} - -.std-card-main:hover { - outline: 2px solid var(--#{vars.$bcgov-prefix}-focus); - cursor: pointer; -} - -.std-card-description { - display: inline-block; -} - -.std-card-header > h5 { - font-weight: 700; - display: block; -} - -.std-card-description > p { - font-size: 0.875rem; - margin-bottom: auto; - color: var(--#{vars.$bcgov-prefix}-text-secondary); -} - -.std-card-pictogram { - width: 8.75rem !important; - height: 8.75rem !important; - padding: 1rem !important; - color: var(--#{vars.$bcgov-prefix}-focus); - position: absolute; - bottom: 0; - left: 0; -} - -.#{vars.$bcgov-prefix}--overflow-menu.#{vars.$bcgov-prefix}--overflow-menu--open >svg { - fill: var(--#{vars.$bcgov-prefix}-icon-primary); -} - -.std-card-header .#{vars.$bcgov-prefix}--popover-container { - height: 2.5rem; -} diff --git a/frontend/src/components/Subtitle/index.tsx b/frontend/src/components/Subtitle/index.tsx index dce04fa1..6a6c435a 100644 --- a/frontend/src/components/Subtitle/index.tsx +++ b/frontend/src/components/Subtitle/index.tsx @@ -1,18 +1,13 @@ import React from 'react'; - import './styles.scss'; interface SubtitleProps { text: string | React.ReactNode; className?: string; - onClick?: () => void; } -const Subtitle = ({ text, className, onClick }: SubtitleProps) => ( -

+const Subtitle = ({ text, className }: SubtitleProps) => ( +

{text}

); diff --git a/frontend/src/components/TableSkeleton/index.tsx b/frontend/src/components/TableSkeleton/index.tsx index 697d7ce0..79aea14b 100644 --- a/frontend/src/components/TableSkeleton/index.tsx +++ b/frontend/src/components/TableSkeleton/index.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React from 'react'; import { DataTableSkeleton } from '@carbon/react'; diff --git a/frontend/src/components/ThemeToggle/index.tsx b/frontend/src/components/ThemeToggle/index.tsx index 8b519828..2f3e4505 100644 --- a/frontend/src/components/ThemeToggle/index.tsx +++ b/frontend/src/components/ThemeToggle/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import './ThemeToggle.scss'; import { AsleepFilled, LightFilled } from '@carbon/icons-react'; import { useThemePreference } from '../../utils/ThemePreference'; @@ -6,7 +6,7 @@ import { toggleTheme } from '../../utils/ThemeFunction'; const ThemeToggle = () => { const { theme, setTheme } = useThemePreference(); - const [isToggled, setIsToggled] = useState(theme === 'g10'?false:true); + const [isToggled, setIsToggled] = useState(theme !== 'g10'); useEffect(()=>{ setIsToggled(theme === 'g10'?false:true); @@ -18,7 +18,17 @@ const ThemeToggle = () => { }; return ( -
+
{ + if (e.key === 'Enter' || e.key === ' ') { + handleToggle(); + } + }} + >
{isToggled ? : }
diff --git a/frontend/src/contexts/NotificationProvider.tsx b/frontend/src/contexts/NotificationProvider.tsx new file mode 100644 index 00000000..92ae8b12 --- /dev/null +++ b/frontend/src/contexts/NotificationProvider.tsx @@ -0,0 +1,87 @@ +import React, { createContext, useState, useContext, useEffect, useMemo, ReactNode } from 'react'; +import {ActionableNotification} from "@carbon/react"; +import {NotificationContent} from "../types/NotificationType"; + + +// 1. Define an interface for the context value +interface NotificationContextType{ + displayNotification: (notification: NotificationContent) => void; +} + +// 2. Define an interface for the provider's props +interface NotificationProviderProps { + children: ReactNode; +} + +// 3. Create the context with a default value of `undefined` +const NotificationContext = createContext(undefined); + +// 4. Create the NotificationProvider component with explicit typing +export const NotificationProvider: React.FC = ({ children }) => { + const [notification, setNotification] = useState(null); + const [isOpen, setIsOpen] = useState(false); + + useEffect(() => { + if(notification){ + setIsOpen(true); + + if(notification.dismissIn){ + setTimeout(() => { + hideNotification(); + }, notification.dismissIn); + } + } + }); + + const displayNotification = (value: NotificationContent) => { + setNotification(value); + setIsOpen(true); + } + + const hideNotification = () => { + setNotification(null); + setIsOpen(false); + } + + const notificationAction = () => { + notification?.onClose(); + hideNotification(); + } + + const contextValue = useMemo(() => ({ + displayNotification + }),[displayNotification]); + + return ( + + {children} + {isOpen && notification && ( + + )} + + ) + +}; + + +// This is a helper hook to use the Notification context more easily +// 5. Create a custom hook to consume the context safely +export const useNotification = () => { + const context = useContext(NotificationContext); + if(!context){ + throw new Error('useNotification must be used within a NotificationProvider'); + } + return context; +} \ No newline at end of file diff --git a/frontend/src/contexts/search/OpeningsSearch.tsx b/frontend/src/contexts/search/OpeningsSearch.tsx index d3aca104..b198c30d 100644 --- a/frontend/src/contexts/search/OpeningsSearch.tsx +++ b/frontend/src/contexts/search/OpeningsSearch.tsx @@ -18,8 +18,8 @@ export const OpeningsSearchProvider: React.FC<{ children: ReactNode }> = ({ chil const defaultFilters = { startDate: null as Date | null, endDate: null as Date | null, - orgUnit: null as string | null, - category: null as string | null, + orgUnit: [] as string[], + category: [] as string[], status: [] as string[], clientAcronym: "", clientLocationCode: "", diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 48e9d8d6..ee654df5 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -2,8 +2,6 @@ window.global ||= window; import React from 'react'; import './index.css'; import { ClassPrefix } from '@carbon/react'; -import { Provider } from 'react-redux' -import store from './store' import App from './App'; import { ThemePreference } from './utils/ThemePreference'; import { createRoot } from 'react-dom/client'; @@ -16,6 +14,7 @@ import amplifyconfig from './amplifyconfiguration'; import { CookieStorage } from 'aws-amplify/utils'; import { cognitoUserPoolsTokenProvider } from 'aws-amplify/auth/cognito'; import { AuthProvider } from './contexts/AuthProvider'; +import { NotificationProvider } from './contexts/NotificationProvider'; const container: HTMLElement | null = document.getElementById('root'); if (container) { @@ -54,14 +53,14 @@ if (container) { - - + - + + + - - + diff --git a/frontend/src/map-services/BcGwLatLongUtils.ts b/frontend/src/map-services/BcGwLatLongUtils.ts index df3d9c35..55eb6498 100644 --- a/frontend/src/map-services/BcGwLatLongUtils.ts +++ b/frontend/src/map-services/BcGwLatLongUtils.ts @@ -34,7 +34,7 @@ export const shiftLineStringCoordinates = (coordinates: number[][]): number[][] for (let i = 0, len = coordinates.length; i < len; i++) { const point = coordinates[i]; if (Array.isArray(point)) { - const newOne = point as number[]; + const newOne = point; newCoord.push([newOne[1], newOne[0]]); } } diff --git a/frontend/src/map-services/BcGwWfsApi.tsx b/frontend/src/map-services/BcGwWfsApi.tsx index 948fa7fe..2829c48b 100644 --- a/frontend/src/map-services/BcGwWfsApi.tsx +++ b/frontend/src/map-services/BcGwWfsApi.tsx @@ -1,6 +1,5 @@ -import { MapLayer } from '../types/MapLayer'; import { OpeningPolygon } from '../types/OpeningPolygon'; -import { shiftBcGwLngLat2LatLng, shiftLineStringCoordinates } from './BcGwLatLongUtils'; +import { shiftBcGwLngLat2LatLng } from './BcGwLatLongUtils'; interface AppendProps { featureProps: object; diff --git a/frontend/src/mock-data/OpeningHistoryItems.ts b/frontend/src/mock-data/OpeningHistoryItems.ts deleted file mode 100644 index 1aeff650..00000000 --- a/frontend/src/mock-data/OpeningHistoryItems.ts +++ /dev/null @@ -1,111 +0,0 @@ -import History from "../types/History"; - -const OpeningHistoryItems: History[] = [ - { - id: 1541297, - steps: [ - { - step: 5, - status: 'invalid', - description: 'Milestone overdue', - subtitle: 'Please, update the milestone' - }, - { - step: 4, - status: 'complete', - description: 'Activity completed', - subtitle: '2023-11-15' - }, - { - step: 3, - status: 'complete', - description: 'Forest cover polygon', - subtitle: '2023-11-02' - }, - { - step: 2, - status: 'complete', - description: 'Disturbance activity', - subtitle: '2023-10-30' - }, - { - step: 1, - status: 'complete', - description: 'Opening ID', - subtitle: '2023-10-18' - } - ] - }, - { - id: 1541298, - steps: [ - { - step: 5, - status: 'complete', - description: 'Forest Cover', - subtitle: 'Now' - }, - { - step: 4, - status: 'complete', - description: 'Activity Planned', - subtitle: 'Now' - }, - { - step: 3, - status: 'complete', - description: 'Forest cover polygon', - subtitle: '2023-10-31' - }, - { - step: 2, - status: 'complete', - description: 'Disturbance Activity', - subtitle: '2023-10-19' - }, - { - step: 1, - status: 'complete', - description: 'Opening ID', - subtitle: '2023-10-01' - } - ] - }, - { - id: 1541299, - steps: [ - { - step: 5, - status: 'invalid', - description: 'Forest cover polygon', - subtitle: 'Please update the forest cover' - }, - { - step: 4, - status: 'invalid', - description: 'Forest cover polygon', - subtitle: 'PLease update the forest cover' - }, - { - step: 3, - status: 'complete', - description: 'Forest cover polygon', - subtitle: '2023-11-01' - }, - { - step: 2, - status: 'complete', - description: 'Disturbance Activity', - subtitle: '2023-10-29' - }, - { - step: 1, - status: 'complete', - description: 'Opening ID', - subtitle: '2023-10-17' - } - ] - } -]; - -export default OpeningHistoryItems; \ No newline at end of file diff --git a/frontend/src/reducers/selectedClientRolesReducer.ts b/frontend/src/reducers/selectedClientRolesReducer.ts deleted file mode 100644 index 8858c3a1..00000000 --- a/frontend/src/reducers/selectedClientRolesReducer.ts +++ /dev/null @@ -1,23 +0,0 @@ -// selectedClientRolesReducer.js -import { SET_SELECTED_CLIENT_ROLES } from "../constants/selectedClientRolesConstants"; -import { UserClientRolesType } from "../types/UserRoleType"; - -const selectedClientRolesFromStorage = JSON.parse(localStorage.getItem('selectedClientRoles') as string) as - | UserClientRolesType - | null; - -const initialState = selectedClientRolesFromStorage ?? null; - -interface ActionType { - type: string; - payload: UserClientRolesType -} - -export const selectedClientRolesReducer = (state = initialState, action: ActionType): UserClientRolesType | null => { - switch (action.type) { - case SET_SELECTED_CLIENT_ROLES: - return action.payload; - default: - return state; - } -}; diff --git a/frontend/src/reducers/userReducer.ts b/frontend/src/reducers/userReducer.ts deleted file mode 100644 index 762d468e..00000000 --- a/frontend/src/reducers/userReducer.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { - USER_DETAILS_REQUEST, - USER_DETAILS_SUCCESS, - USER_DETAILS_FAIL, - SET_CLIENT_ROLES -} from "../constants/userConstants"; - -const initialState = { - user: { - clientRoles: [], - selectedClientRoles: null - }, - loading: false, - error: null -}; - -export const userDetailsReducer = (state = initialState, action: any) => { - switch (action.type) { - case USER_DETAILS_REQUEST: - return { ...state, loading: true }; - case USER_DETAILS_SUCCESS: - return { ...state, loading: false, user: action.payload }; - case USER_DETAILS_FAIL: - return { ...state, loading: false, error: action.payload }; - case SET_CLIENT_ROLES: - return { - ...state, - user: { ...state.user, clientRoles: action.payload } - }; - default: - return state; - } -}; \ No newline at end of file diff --git a/frontend/src/screens/DashboardRedirect/index.tsx b/frontend/src/screens/DashboardRedirect/index.tsx index c8e32c9f..7a4d41dd 100644 --- a/frontend/src/screens/DashboardRedirect/index.tsx +++ b/frontend/src/screens/DashboardRedirect/index.tsx @@ -1,13 +1,33 @@ import React from "react"; -import { Navigate } from "react-router-dom"; -import { Loading } from "@carbon/react"; -import { useGetAuth } from "../../contexts/AuthProvider"; +import { useEffect } from "react"; +import { useNavigate } from "react-router-dom"; +import LoginOrgSelection from "../../views/LoginOrgSelection"; +import SideLayout from "../../layouts/SideLayout"; +import Opening from "../Opening"; +import { RootState } from "../../store"; +import { useSelector } from "react-redux"; const DashboardRedirect: React.FC = () => { - const { isLoggedIn } = useGetAuth(); + const userDetails = useSelector((state: RootState) => state.userDetails); + const selectedClientRoles = useSelector((state:any)=>state.selectedClientRoles) + const { user } = userDetails; + + const navigate = useNavigate(); + + // Redirect logic based on selectedClientRoles existence + useEffect(() => { + if (user && selectedClientRoles) { + navigate("/opening"); + } + }, [user, selectedClientRoles]); + return ( <> - {isLoggedIn ? : } + {user && selectedClientRoles ? ( + } /> + ) : ( + + )} ); }; diff --git a/frontend/src/screens/Help/Help.scss b/frontend/src/screens/Help/Help.scss deleted file mode 100644 index f0ac8f28..00000000 --- a/frontend/src/screens/Help/Help.scss +++ /dev/null @@ -1,12 +0,0 @@ -@use '@carbon/layout'; - -.help-grid{ - height: 80vh; - align-items: center; - text-align: center; -} - - -.help-back-btn { - margin-top: layout.$spacing-05; - } \ No newline at end of file diff --git a/frontend/src/screens/Help/index.tsx b/frontend/src/screens/Help/index.tsx deleted file mode 100644 index e037e7a3..00000000 --- a/frontend/src/screens/Help/index.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React from "react"; -import { NavLink } from 'react-router-dom'; -import { Button } from "@carbon/react"; -import { ArrowLeft } from '@carbon/icons-react'; - -import './Help.scss'; - -const Help: React.FC = () => { - return ( - <> -
-
-
-
Help Page
-

Welcome to the Help Page

-

This is just for the purpose of templating React Routing!

- - - -
-
-
- - ); - }; - -export default Help; \ No newline at end of file diff --git a/frontend/src/screens/Landing/index.tsx b/frontend/src/screens/Landing/index.tsx index 22d0668f..4c9416a9 100644 --- a/frontend/src/screens/Landing/index.tsx +++ b/frontend/src/screens/Landing/index.tsx @@ -25,58 +25,56 @@ const Landing: React.FC = () => { } return ( - <> -
-
-
-
- -
+
+
+
+
+ +
- {/* Welcome - Title and Subtitle */} -
-

Welcome to SILVA

-

- Plan, report, and analyze your silviculture activities -

+ {/* Welcome - Title and Subtitle */} +
+

Welcome to SILVA

+

+ Plan, report, and analyze your silviculture activities +

+
+ {/* Button Group */} +
+
+
- {/* Button Group */} -
-
- -
-
- -
+
+
+
+
+
+
+ {View}
-
- +
); }; diff --git a/frontend/src/screens/Opening/index.tsx b/frontend/src/screens/Opening/index.tsx index 131ee0fc..7ecdc3be 100644 --- a/frontend/src/screens/Opening/index.tsx +++ b/frontend/src/screens/Opening/index.tsx @@ -2,16 +2,23 @@ import React, {useEffect, useState} from "react"; import FavouriteCard from "../../components/FavouriteCard"; import PageTitle from "../../components/PageTitle"; import './Opening.scss' -import { TabList } from "@carbon/react"; -import { Tabs } from "@carbon/react"; -import { Tab } from "@carbon/react"; -import { TabPanels } from "@carbon/react"; -import { TabPanel } from "@carbon/react"; +import { + TabList, + Tabs, + Tab, + TabPanels, + TabPanel +} from "@carbon/react"; import OpeningsTab from "../../components/OpeningsTab"; import OpeningMetricsTab from "../../components/OpeningMetricsTab"; const Opening: React.FC = () => { const [showSpatial, setShowSpatial] = useState(false); + const [activeTab, setActiveTab] = useState(0); // Track active tab index + + const tabChange = (tabSelection:{selectedIndex: number}) => { + setActiveTab(tabSelection.selectedIndex); + }; useEffect(() => { // @@ -74,20 +81,20 @@ const Opening: React.FC = () => {
)} - +
Recent Openings
Dashboard
- + - - + + {activeTab === 1 && }
diff --git a/frontend/src/screens/Reports/Reports.scss b/frontend/src/screens/Reports/Reports.scss deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/src/screens/Reports/index.tsx b/frontend/src/screens/Reports/index.tsx deleted file mode 100644 index 38c092d2..00000000 --- a/frontend/src/screens/Reports/index.tsx +++ /dev/null @@ -1,196 +0,0 @@ -import React from 'react'; -import { - DatePicker, - DatePickerInput, - Dropdown, - Table, - TableHead, - TableHeader, - TableRow, - TableBody, - TableCell, - ModalWrapper - } from '@carbon/react'; -import './Reports.scss'; - -/** - * Reports component. - * - * This component renders a page with a sample form and a table. - * - * @returns {JSX.Element} The Reports component. - */ -function Reports(): JSX.Element { - const items: string[] = ["Apple", "Mango", "Orange", "Peach"]; - - const rows:any[] = [ - { - id: 'load-balancer-1', - name: 'Load Balancer 1', - rule: 'Round robin', - Status: 'Starting', - other: 'Test', - example: '22', - }, - { - id: 'load-balancer-2', - name: 'Load Balancer 2', - rule: 'DNS delegation', - status: 'Active', - other: 'Test', - example: '22', - }, - { - id: 'load-balancer-3', - name: 'Load Balancer 3', - rule: 'Round robin', - status: 'Disabled', - other: 'Test', - example: '22', - }, - { - id: 'load-balancer-4', - name: 'Load Balancer 4', - rule: 'Round robin', - status: 'Disabled', - other: 'Test', - example: '22', - }, - { - id: 'load-balancer-5', - name: 'Load Balancer 5', - rule: 'Round robin', - status: 'Disabled', - other: 'Test', - example: '22', - }, - { - id: 'load-balancer-6', - name: 'Load Balancer 6', - rule: 'Round robin', - status: 'Disabled', - other: 'Test', - example: '22', - }, - { - id: 'load-balancer-7', - name: 'Load Balancer 7', - rule: 'Round robin', - status: 'Disabled', - other: 'Test', - example: '22', - }, - ]; - - const headers:any[] = ['Name', 'Rule', 'Status', 'Other', 'Example']; - - return ( -
-
-
-
Reports Page
-
-
- -
Form Sample
- -
-
- - - - -
-
- (item ? item : '')} - /> -
-
- -
-
- (item ? item : '')} - /> -
-
- (item ? item : '')} - /> -
-
- (item ? item : '')} - /> -
-
- -
- {}} - > -

Modal content here

-
-
- -
-
- - - - {headers.map((header) => ( - - {header} - - ))} - - - - {rows.map((row) => ( - - {Object.keys(row) - .filter((key) => key !== 'id') - .map((key) => { - return {row[key]}; - })} - - ))} - -
-
-
-
- ); -}; - -export default Reports; diff --git a/frontend/src/screens/SilvicultureSearch/index.tsx b/frontend/src/screens/SilvicultureSearch/index.tsx index faa59ccb..c50dae59 100644 --- a/frontend/src/screens/SilvicultureSearch/index.tsx +++ b/frontend/src/screens/SilvicultureSearch/index.tsx @@ -1,14 +1,15 @@ import React from "react"; import PageTitle from "../../components/PageTitle"; import './SilvicultureSearch.scss' -import { TabList } from "@carbon/react"; -import { Tabs } from "@carbon/react"; -import { Tab } from "@carbon/react"; -import { TabPanels } from "@carbon/react"; -import { TabPanel } from "@carbon/react"; +import { + TabList, + Tabs, + Tab, + TabPanels, + TabPanel +} from "@carbon/react"; import * as Icons from '@carbon/icons-react'; import OpeningsSearchTab from "../../components/SilvicultureSearch/Openings/OpeningsSearchTab"; -import { OpeningsSearchProvider } from "../../contexts/search/OpeningsSearch"; const SilvicultureSearch: React.FC = () => { @@ -26,9 +27,9 @@ const SilvicultureSearch: React.FC = () => {
Openings
-
Activities
-
Stocking standards
-
Standard units
+
Activities
+
Stocking standards
+
Standard units
diff --git a/frontend/src/services/OpeningFavoriteService.ts b/frontend/src/services/OpeningFavoriteService.ts new file mode 100644 index 00000000..5c85d5e3 --- /dev/null +++ b/frontend/src/services/OpeningFavoriteService.ts @@ -0,0 +1,73 @@ +import axios from 'axios'; +import { getAuthIdToken } from './AuthService'; +import { env } from '../env'; + +const backendUrl = env.VITE_BACKEND_URL; + +/** + * Fetches the submission trends/favorites from the backend. + * + * This function sends a GET request to the backend API to retrieve the user favorite openings. + * It includes an authorization token in the request headers. + * + * @returns {Promise} A promise that resolves to an array of numbers representing the opening ids. + * If the response data is not an array, it returns an empty array. + */ +export const fetchOpeningTrends = async (): Promise =>{ + const authToken = getAuthIdToken(); + const response = await axios.get( + `${backendUrl}/api/openings/favorites`, { + headers: { + Authorization: `Bearer ${authToken}` + } + }); + + const { data } = response; + if (data && Array.isArray(data)) { + return data; + } else { + return []; + } +} + +/** + * Sets an opening as a favorite for the authenticated user. + * + * @param {number} openingId - The ID of the opening to be set as favorite. + * @returns {Promise} A promise that resolves when the operation is complete. + * @throws {Error} Throws an error if the request fails with a status code other than 202. + */ +export const setOpeningFavorite = async (openingId: number): Promise => { + const authToken = getAuthIdToken(); + const response = await axios.put( + `${backendUrl}/api/openings/favorites/${openingId}`, null, { + headers: { + Authorization: `Bearer ${authToken}` + } + }); + + if (response.status !== 202) { + throw new Error(`Failed to set favorite opening. Status code: ${response.status}`); + } +} + +/** + * Deletes a favorite opening by its ID. + * + * @param {number} openingId - The ID of the opening to be removed from favorites. + * @returns {Promise} A promise that resolves when the favorite opening is successfully deleted. + * @throws {Error} Throws an error if the deletion fails or the response status is not 204. + */ +export const deleteOpeningFavorite = async (openingId: number): Promise => { + const authToken = getAuthIdToken(); + const response = await axios.delete( + `${backendUrl}/api/openings/favorites/${openingId}`, { + headers: { + Authorization: `Bearer ${authToken}` + } + }); + + if (response.status !== 204) { + throw new Error(`Failed to remove favorite opening. Status code: ${response.status}`); + } +} \ No newline at end of file diff --git a/frontend/src/services/OpeningService.ts b/frontend/src/services/OpeningService.ts index 81cda2e7..324fdaa3 100644 --- a/frontend/src/services/OpeningService.ts +++ b/frontend/src/services/OpeningService.ts @@ -4,79 +4,10 @@ import { env } from '../env'; import { RecentAction } from '../types/RecentAction'; import { OpeningPerYearChart } from '../types/OpeningPerYearChart'; import { RecentOpening } from '../types/RecentOpening'; +import { IOpeningPerYear } from '../types/IOpeningPerYear'; const backendUrl = env.VITE_BACKEND_URL; -interface statusCategory { - code: string; - description: string; -} - -interface RecentOpeningApi { - openingId: number; - forestFileId: string; - cuttingPermit: string | null; - timberMark: string | null; - cutBlock: string | null; - grossAreaHa: number | null; - status: statusCategory | null; - category: statusCategory | null; - disturbanceStart: string | null; - entryTimestamp: string | null; - updateTimestamp: string | null; -} - -/** - * Fetch recent openings data from backend. - * - * @returns {Promise} Array of objects found - */ -export async function fetchRecentOpenings(): Promise { - const authToken = getAuthIdToken(); - try { - const response = await axios.get(backendUrl.concat("/api/openings/recent-openings?page=0&perPage=100"), { - headers: { - Authorization: `Bearer ${authToken}` - } - }); - - if (response.status >= 200 && response.status < 300) { - const { data } = response; - - if (data.data) { - // Extracting row information from the fetched data - const rows: RecentOpening[] = data.data.map((opening: RecentOpeningApi) => ({ - id: opening.openingId.toString(), - openingId: opening.openingId.toString(), - forestFileId: opening.forestFileId ? opening.forestFileId : '-', - cuttingPermit: opening.cuttingPermit ? opening.cuttingPermit : '-', - timberMark: opening.timberMark ? opening.timberMark : '-', - cutBlock: opening.cutBlock ? opening.cutBlock : '-', - grossAreaHa: opening.grossAreaHa ? opening.grossAreaHa.toString() : '-', - status: opening.status && opening.status.description? opening.status.description : '-', - category: opening.category && opening.category.description? opening.category.description : '-', - disturbanceStart: opening.disturbanceStart ? opening.disturbanceStart : '-', - entryTimestamp: opening.entryTimestamp ? opening.entryTimestamp.split('T')[0] : '-', - updateTimestamp: opening.updateTimestamp ? opening.updateTimestamp.split('T')[0] : '-' - })); - - return rows; - } - } - return []; - } catch (error) { - console.error('Error fetching recent openings:', error); - throw error; - } -} - -interface IOpeningPerYear { - orgUnitCode: string | null; - statusCode: string | null; - entryDateStart: string | null; - entryDateEnd: string | null; -} - /** * Fetch openings per year data from backend. * @@ -122,6 +53,39 @@ export async function fetchOpeningsPerYear(props: IOpeningPerYear): Promise => { + const authToken = getAuthIdToken(); + + try { + let url = `${backendUrl}/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}&`; + url = url.replace(/&$/, ""); + } + + const response = await axios.get(url, { + headers: { Authorization: `Bearer ${authToken}` } + }); + + if (response.data && Array.isArray(response.data)) { + return response.data.map(item => ({ + group: "Openings", + key: item.monthName, + value: item.amount + })); + } + + return []; + } catch (error) { + console.error("Error fetching openings per year:", error); + throw error; + } +}; + interface IFreeGrowingProps { orgUnitCode: string; clientNumber: string; diff --git a/frontend/src/services/queries/dashboard/dashboardQueries.ts b/frontend/src/services/queries/dashboard/dashboardQueries.ts new file mode 100644 index 00000000..622b5f88 --- /dev/null +++ b/frontend/src/services/queries/dashboard/dashboardQueries.ts @@ -0,0 +1,52 @@ +import { useMutation, useQuery } from "@tanstack/react-query"; +import axios from "axios"; +import { getAuthIdToken } from "../../AuthService"; +import { fetchOpeningsPerYearAPI } from "../../OpeningService"; +import { IOpeningPerYear } from "../../../types/IOpeningPerYear"; +import { fetchOrgUnits } from "../../search/openings"; + +const backendUrl = import.meta.env.VITE_BACKEND_URL; + +// Function to send the POST request +export const postViewedOpening = async (openingId: string): Promise => { + const authToken = getAuthIdToken(); + try { + const response = await axios.post(`${backendUrl}/viewed/${openingId}`, null, { + headers: { + Authorization: `Bearer ${authToken}`, + }, + }); + return response.data; + } catch (error:any) { + if (error.response?.status === 403) { + throw new Error("Forbidden: You don't have permission to view this opening."); + } else { + throw new Error(error.response.data.message); + } + } + }; + + // Hook for using the mutation + export const usePostViewedOpening = () => { + return useMutation({ + mutationFn: (openingId: string) => postViewedOpening(openingId), + }); + }; + +// Custom hook to use in your component +export const useFetchOpeningsPerYear = (props: IOpeningPerYear) => { + return useQuery({ + queryKey: ['openingsPerYear', props], // Cache key including props + queryFn: () => fetchOpeningsPerYearAPI(props), // Fetch function + enabled: true, // For Conditional fetch we can use !!props.orgUnitCode || !!props.statusCode || !!props.entryDateStart || !!props.entryDateEnd + staleTime: 5 * 60 * 1000, // Cache duration (optional) + }); +}; + +export const useDistrictListQuery = () => { + return useQuery({ + queryKey: ["districtList"], + queryFn: fetchOrgUnits + }); +}; + diff --git a/frontend/src/services/queries/search/openingQueries.ts b/frontend/src/services/queries/search/openingQueries.ts index 4f5a6ebe..dd966796 100644 --- a/frontend/src/services/queries/search/openingQueries.ts +++ b/frontend/src/services/queries/search/openingQueries.ts @@ -1,5 +1,5 @@ import { useQuery } from "@tanstack/react-query"; -import { fetchOpeningFilters, fetchOpenings, OpeningFilters } from "../../search/openings"; +import { fetchOpeningFilters, fetchOpenings, fetchUserRecentOpenings, OpeningFilters } from "../../search/openings"; export const useOpeningsQuery = (filters: OpeningFilters, enabled: boolean) => { return useQuery({ @@ -9,6 +9,15 @@ export const useOpeningsQuery = (filters: OpeningFilters, enabled: boolean) => { }); }; +export const useUserRecentOpeningQuery = (limit:number) => { + return useQuery({ + queryKey: ["userRecentOpenings"], + queryFn: () => fetchUserRecentOpenings(limit), + enabled: true, + refetchOnMount: "always" + }); +}; + export const useOpeningFiltersQuery = () => { return useQuery({ queryKey: ["openingFilters"], diff --git a/frontend/src/services/search/openings.ts b/frontend/src/services/search/openings.ts index 59c87b9f..af648373 100644 --- a/frontend/src/services/search/openings.ts +++ b/frontend/src/services/search/openings.ts @@ -9,10 +9,11 @@ export interface OpeningFilters { searchInput?: string; startDate?: string; endDate?: string; - orgUnit?: string; - category?: string; + orgUnit?: string[]; + category?: string[]; clientAcronym?: string; blockStatus?: string; + dateType?: string; cutBlock?: string; cuttingPermit?: string; grossArea?: string; @@ -62,12 +63,12 @@ export const fetchOpenings = async (filters: OpeningFilters): Promise => { const params = { mainSearchTerm: filters.searchInput, - orgUnit: filters.orgUnit, - category: filters.category, + orgUnit: filters.orgUnit, //Keep it as an array + category: filters.category, // Keep it as an array statusList: filters.status, // Keep it as an array entryUserId: filters.clientAcronym, cutBlockId: filters.cutBlock, - cuttinPermitId:filters.cuttingPermit, + cuttingPermitId:filters.cuttingPermit, timbermark:filters.timberMark, myOpenings: filters.openingFilters?.includes("Openings created by me") || undefined, @@ -118,6 +119,36 @@ export const fetchOpenings = async (filters: OpeningFilters): Promise => { }; }; +// Used to fetch the recent openings for a user based on a limit value +export const fetchUserRecentOpenings = async (limit: number): Promise => { + + // Retrieve the auth token + const authToken = getAuthIdToken(); + + // Make the API request with the Authorization header + const response = await axios.get(`${backendUrl}/api/openings/recent`, { + headers: { + Authorization: `Bearer ${authToken}` + } + }); + + // Flatten the data part of the response + const flattenedData = response.data.data.map((item: OpeningItem) => ({ + ...item, + statusCode: item.status?.code, + statusDescription: item.status?.description, + categoryCode: item.category?.code, + categoryDescription: item.category?.description, + status: undefined, // Remove the old nested status object + category: undefined // Remove the old nested category object + })); + + // Returning the modified response data with the flattened structure + return { + ...response.data, + data: flattenedData + }; +}; export const fetchCategories = async (): Promise => { // Retrieve the auth token diff --git a/frontend/src/store.ts b/frontend/src/store.ts deleted file mode 100644 index edba420c..00000000 --- a/frontend/src/store.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { createStore, combineReducers, applyMiddleware } from 'redux' -import { thunk } from 'redux-thunk' -import { composeWithDevTools } from '@redux-devtools/extension' -import { userDetailsReducer } from './reducers/userReducer' -import { UserClientRolesType } from './types/UserRoleType' -import { selectedClientRolesReducer } from './reducers/selectedClientRolesReducer' -import { FamLoginUser } from './services/AuthService' - -const reducer = combineReducers({ - userDetails: userDetailsReducer, - selectedClientRoles: selectedClientRolesReducer -}); - -const FAM_LOGIN_USER = 'famLoginUser'; - - -const userInfoFromStorage = JSON.parse(localStorage.getItem(FAM_LOGIN_USER) as string) as - | FamLoginUser - | undefined - | null; - -const selectedClientRolesFromStorage = JSON.parse(localStorage.getItem('selectedClientRoles') as string) as - | UserClientRolesType - | undefined - | null; - -// set the initial state -const initialState: object = { - userDetails: { - user: { - ...userInfoFromStorage, - isLoggedIn: !!userInfoFromStorage?.authToken - }, - loading: true, - error: false - }, - selectedClientRoles: selectedClientRolesFromStorage -}; - -const middleware = [thunk]; - -const store = createStore( - reducer, - initialState, - composeWithDevTools(applyMiddleware(...middleware)) -); - -export type RootState = ReturnType; -export type AppDispatch = typeof store.dispatch; - -export default store; diff --git a/frontend/src/types/IOpeningPerYear.ts b/frontend/src/types/IOpeningPerYear.ts new file mode 100644 index 00000000..4add109d --- /dev/null +++ b/frontend/src/types/IOpeningPerYear.ts @@ -0,0 +1,6 @@ +export interface IOpeningPerYear { + orgUnitCode: string | null; + statusCode: string | null; + entryDateStart: string | null; + entryDateEnd: string | null; + } \ No newline at end of file diff --git a/frontend/src/types/NotificationType.ts b/frontend/src/types/NotificationType.ts new file mode 100644 index 00000000..0e0e3bd5 --- /dev/null +++ b/frontend/src/types/NotificationType.ts @@ -0,0 +1,8 @@ +export interface NotificationContent { + title: string; + subTitle: string; + buttonLabel?: string; + dismissIn?: number; + type: 'info' | 'error' | 'success' | 'warning' | 'info-square' | 'error-square' | 'warning-alt'; + onClose: () => void; +} \ No newline at end of file diff --git a/frontend/src/types/OpeningTypes.ts b/frontend/src/types/OpeningTypes.ts new file mode 100644 index 00000000..34969a73 --- /dev/null +++ b/frontend/src/types/OpeningTypes.ts @@ -0,0 +1,37 @@ +export interface StatusCategory { + code: string; + description: string; +} + +export interface RecentOpeningApi { + openingId: number; + forestFileId: string; + cuttingPermit: string | null; + timberMark: string | null; + cutBlock: string | null; + grossAreaHa: number | null; + status: StatusCategory | null; + category: StatusCategory | null; + disturbanceStart: string | null; + entryTimestamp: string | null; + updateTimestamp: string | null; +} + +export interface IOpeningPerYear { + orgUnitCode: string | null; + statusCode: string | null; + entryDateStart: string | null; + entryDateEnd: string | null; +} + +export interface IFreeGrowingProps { + orgUnitCode: string; + clientNumber: string; + entryDateStart: string | null; + entryDateEnd: string | null; +} + +export interface IFreeGrowingChartData { + group: string; + value: number; +} \ No newline at end of file diff --git a/frontend/src/utils/DateUtils.ts b/frontend/src/utils/DateUtils.ts index a5b416ce..44b66e3d 100644 --- a/frontend/src/utils/DateUtils.ts +++ b/frontend/src/utils/DateUtils.ts @@ -12,3 +12,11 @@ export const dateStringToISO = (date: string): string => { } return ''; }; + +export const formatDateToString = (dateToFormat: Date) => { + if (!dateToFormat) return null; + const year = dateToFormat.getFullYear(); + const month = String(dateToFormat.getMonth() + 1).padStart(2, "0"); + const day = String(dateToFormat.getDate()).padStart(2, "0"); + return `${year}-${month}-${day}`; +}; diff --git a/frontend/src/utils/ThemePreference.tsx b/frontend/src/utils/ThemePreference.tsx index fb95ab1e..bddecb21 100644 --- a/frontend/src/utils/ThemePreference.tsx +++ b/frontend/src/utils/ThemePreference.tsx @@ -20,7 +20,7 @@ function useThemePreference() { } interface ThemePreferenceProps { - children?: ReactNode + readonly children?: ReactNode } /** diff --git a/frontend/src/views/LoginOrgSelection/index.tsx b/frontend/src/views/LoginOrgSelection/index.tsx deleted file mode 100644 index 901782e2..00000000 --- a/frontend/src/views/LoginOrgSelection/index.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React, { useEffect } from 'react'; -import { FlexGrid, Row, Column } from '@carbon/react'; -import OrganizationSelection from '../../components/OrganizationSelection'; -import { RootState } from '../../store'; -import { useSelector } from 'react-redux'; -import './styles.scss'; - -/** - * Renders the organization selection view after login. - * - * This view allows users to select which organization they are representing. - * It displays the user's name and username, prompting them to choose an organization. - * - * @returns {JSX.Element} The rendered LoginOrgSelection component. - */ -function LoginOrgSelection (): JSX.Element { - const userDetails = useSelector((state: RootState) => state.userDetails); - const user = userDetails.user; - - return ( - - - -
-

- Organization selection -

-

- {`${user?.firstName} ${user?.lastName} ${user.providerUsername ? "("+user.providerUsername+")": ""} select which organization you're representing.`} -

-
- -
-
-
- ); -}; - -export default LoginOrgSelection; diff --git a/frontend/src/views/LoginOrgSelection/styles.scss b/frontend/src/views/LoginOrgSelection/styles.scss deleted file mode 100644 index db688f12..00000000 --- a/frontend/src/views/LoginOrgSelection/styles.scss +++ /dev/null @@ -1,48 +0,0 @@ -@use '@carbon/type'; -@use '@bcgov-nr/nr-theme/design-tokens/variables.scss' as vars; - -.login-org-selection-grid { - background: lightgray; // temporary change for demo linear-gradient(180deg, #E0EFFF 0%, #F2EEDB 100%); - height: 100vh; - width: 100%; - display: flex; - justify-content: center; - align-items: center; - margin-inline: 0; - max-inline-size: none; - - .row-container { - max-width: none; - width: 100%; - display: flex; - justify-content: center; - align-items: center; - } - - .col-container { - display: flex; - flex-direction: column; - padding: min(4.63%, 3rem) min(4.63%, 4rem); - border-radius: 0.5rem; - background: var(--bx-layer-02); - /* Light Theme/Shadows/Menu */ - box-shadow: 0rem 0.125rem 0.375rem 0rem rgba(0, 0, 0, 0.30); - min-height: 50vh; - } - - .title-section { - text-align: left; - margin-bottom: 2.5rem; - - .title-text { - margin-bottom: 1rem; - font-size: 2.625rem; - } - - .subtitle-text { - @include type.type-style('body-01'); - color: var(--#{vars.$bcgov-prefix}-text-secondary); - font-size: 1rem; - } - } -} diff --git a/stub/__files/forestclient/findByClientNumber_00149081.json b/stub/__files/forestclient/findByClientNumber_00149081.json new file mode 100644 index 00000000..cbec2a7f --- /dev/null +++ b/stub/__files/forestclient/findByClientNumber_00149081.json @@ -0,0 +1,7 @@ +{ + "clientNumber": "00149081", + "clientName": "PAULO CORPORATION OF MARS", + "clientStatusCode": "ACT", + "clientTypeCode": "F", + "acronym": "PGCJ" +} \ No newline at end of file diff --git a/stub/mappings/forestclient_mapping.json b/stub/mappings/forestclient_mapping.json index 47cfb36b..e1f20145 100644 --- a/stub/mappings/forestclient_mapping.json +++ b/stub/mappings/forestclient_mapping.json @@ -8,6 +8,9 @@ }, "response": { "status": 200, + "headers": { + "Content-Type": "application/json" + }, "transformers": [ "response-template" ],