diff --git a/.github/workflows/code_check_frontend.yml b/.github/workflows/code_check_frontend.yml index a80098736..8e5da2f79 100644 --- a/.github/workflows/code_check_frontend.yml +++ b/.github/workflows/code_check_frontend.yml @@ -18,7 +18,7 @@ jobs: uses: actions/checkout@v4 - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: govtool/frontend/node_modules key: ${{ runner.os }}-node-${{ hashFiles('govtool/frontend/package-lock.json') }} @@ -44,7 +44,7 @@ jobs: uses: actions/checkout@v4 - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: govtool/frontend/node_modules key: ${{ runner.os }}-node-${{ hashFiles('govtool/frontend/package-lock.json') }} @@ -70,7 +70,7 @@ jobs: uses: actions/checkout@v4 - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: govtool/frontend/node_modules key: ${{ runner.os }}-node-${{ hashFiles('govtool/frontend/package-lock.json') }} diff --git a/.github/workflows/frontend_sonar_scan.yml b/.github/workflows/frontend_sonar_scan.yml index 323c83f65..1dcded146 100644 --- a/.github/workflows/frontend_sonar_scan.yml +++ b/.github/workflows/frontend_sonar_scan.yml @@ -17,7 +17,7 @@ jobs: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: govtool/frontend/node_modules key: ${{ runner.os }}-node-${{ hashFiles('govtool/frontend/package-lock.json') }} diff --git a/.github/workflows/merge.yaml b/.github/workflows/merge.yaml index 065a39624..3dd137070 100644 --- a/.github/workflows/merge.yaml +++ b/.github/workflows/merge.yaml @@ -102,7 +102,7 @@ jobs: uses: docker/setup-buildx-action@v2 - name: Cache Docker layers - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: /tmp/.buildx-cache key: ${{ runner.os }}-buildx-${{ github.sha }} diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 2ec98149c..eed67c623 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -74,7 +74,6 @@ jobs: set -o pipefail sudo chmod +x lint.sh && ./lint.sh 2>&1 | tee code_lint_output.txt - - name: Unit tests id: unit_tests run: | @@ -90,7 +89,7 @@ jobs: uses: docker/setup-buildx-action@v2 - name: Cache Docker layers - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: /tmp/.buildx-cache key: ${{ runner.os }}-buildx-${{ github.sha }} @@ -127,19 +126,19 @@ jobs: VITE_IPFS_PROJECT_ID=${{ secrets.IPFS_PROJECT_ID }} IPFS_GATEWAY=${{ secrets.IPFS_GATEWAY }} IPFS_PROJECT_ID=${{ secrets.IPFS_PROJECT_ID }} - + - name: Scan Docker image with Dockle id: dockle run: | - wget -q https://github.com/goodwithtech/dockle/releases/download/v0.4.14/dockle_0.4.14_Linux-64bit.tar.gz - tar zxf dockle_0.4.14_Linux-64bit.tar.gz - sudo mv dockle /usr/local/bin + wget -q https://github.com/goodwithtech/dockle/releases/download/v0.4.14/dockle_0.4.14_Linux-64bit.tar.gz + tar zxf dockle_0.4.14_Linux-64bit.tar.gz + sudo mv dockle /usr/local/bin - dockle --exit-code 1 --exit-level fatal -ak GHC_RELEASE_KEY -ak CABAL_INSTALL_RELEASE_KEY -ak STACK_RELEASE_KEY -ak KEY_SHA512 --format json --input '/tmp/image-${{ matrix.name }}-${{ github.sha }}-pr.tar' --output ${{ matrix.workdir }}/dockle_scan_output.json - rm -rf '/tmp/image-${{ matrix.name }}-${{ github.sha }}-pr.tar' - cat ${{ matrix.workdir }}/dockle_scan_output.json + dockle --exit-code 1 --exit-level fatal -ak GHC_RELEASE_KEY -ak CABAL_INSTALL_RELEASE_KEY -ak STACK_RELEASE_KEY -ak KEY_SHA512 --format json --input '/tmp/image-${{ matrix.name }}-${{ github.sha }}-pr.tar' --output ${{ matrix.workdir }}/dockle_scan_output.json + rm -rf '/tmp/image-${{ matrix.name }}-${{ github.sha }}-pr.tar' + cat ${{ matrix.workdir }}/dockle_scan_output.json - echo "outcome=success" >> $GITHUB_OUTPUT + echo "outcome=success" >> $GITHUB_OUTPUT - name: Create PR comment if: always() diff --git a/.github/workflows/test_integration_playwright.yml b/.github/workflows/test_integration_playwright.yml index cf5e0ed63..bbb9c9052 100644 --- a/.github/workflows/test_integration_playwright.yml +++ b/.github/workflows/test_integration_playwright.yml @@ -31,7 +31,7 @@ on: workflow_run: workflows: ["Build and deploy GovTool test stack"] types: [completed] - + concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: false @@ -39,7 +39,7 @@ concurrency: jobs: integration-tests: runs-on: ubuntu-latest - if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }} + if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }} outputs: start_time: ${{ steps.set-pending-status.outputs.timestamp }} status: ${{ steps.run-test.outcome }} @@ -49,7 +49,7 @@ jobs: steps: - uses: actions/checkout@v4 with: - ref: ${{ env.COMMIT_SHA }} + ref: ${{ env.COMMIT_SHA }} - name: Set pending commit status id: set-pending-status run: | @@ -70,7 +70,7 @@ jobs: - name: Cache Playwright browsers id: cache-playwright-browsers - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ~/.cache/ms-playwright @@ -132,7 +132,7 @@ jobs: publish-report: runs-on: ubuntu-latest - if: always() && needs.integration-tests.result != 'skipped' + if: always() && needs.integration-tests.result != 'skipped' needs: integration-tests outputs: report_number: ${{ steps.report-details.outputs.report_number }} diff --git a/.github/workflows/update-govtool-version.yml b/.github/workflows/update-govtool-version.yml new file mode 100644 index 000000000..c7156e268 --- /dev/null +++ b/.github/workflows/update-govtool-version.yml @@ -0,0 +1,102 @@ +name: Update GovTool Version and Changelog + +on: + workflow_dispatch: + inputs: + version: + description: "New version (e.g., 1.0.0)" + required: true + +jobs: + update-version: + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + registry-url: "https://registry.npmjs.org/" + node-version-file: "./govtool/frontend/.nvmrc" + scope: "@intersect.mbo" + + - name: Set Version Variable + run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV + + - name: Update package.json files and install dependencies + run: | + jq --arg v "$VERSION" '.version = $v' govtool/frontend/package.json > temp.json && mv temp.json govtool/frontend/package.json + jq --arg v "$VERSION" '.version = $v' govtool/metadata-validation/package.json > temp.json && mv temp.json govtool/metadata-validation/package.json + + echo "Running npm install to update lock files..." + cd govtool/frontend && npm install && cd ../.. + cd govtool/metadata-validation && npm install && cd ../.. + + - name: Update vva-be.cabal version (Preserve Spacing) + run: | + sed -i -E "s/^(version:[[:space:]]+)[0-9]+\.[0-9]+\.[0-9]+/\1$VERSION/" govtool/backend/vva-be.cabal + echo "✅ Updated version in vva-be.cabal while preserving spacing." + + - name: Update Dockerfile Versions + run: | + sed -i -E "s/vva-be-[0-9]+\.[0-9]+\.[0-9]+/vva-be-$VERSION/g" govtool/backend/Dockerfile + sed -i -E "s/vva-be-[0-9]+\.[0-9]+\.[0-9]+/vva-be-$VERSION/g" govtool/backend/Dockerfile.qovery + echo "✅ Updated vva-be version in Dockerfiles." + + - name: Update Metadata Validation API Version + run: | + sed -i -E "s/(\.setVersion\()[\"'][0-9]+\.[0-9]+\.[0-9]+[\"']/\1\"$VERSION\"/" govtool/metadata-validation/src/main.ts + echo "✅ Updated API version in main.ts" + + - name: Update CHANGELOG.md + run: | + #!/bin/bash + VERSION="${{ github.event.inputs.version }}" + TODAY=$(date +%Y-%m-%d) + RELEASE_TAG="v$VERSION" + RELEASE_LINK="[v$VERSION](https://github.com/IntersectMBO/govtool/releases/tag/$RELEASE_TAG)" + TEMP_FILE=$(mktemp) + + echo "Updating CHANGELOG.md with version $VERSION and date $TODAY..." + + awk -v rl="$RELEASE_LINK" -v td="$TODAY" ' + BEGIN { unreleased_found = 0; print_new_unreleased = 1 } + { + if ($1 == "##" && $2 == "[Unreleased]") { + unreleased_found = 1 + + # Print the new Unreleased section with required sub-sections + print "## [Unreleased]\n" + print "### Added\n" + print "### Fixed\n" + print "### Changed\n" + print "### Removed\n" + + # Rename the old Unreleased section with a version and date + print "## " rl " " td "\n" + next + } + print + }' CHANGELOG.md > "$TEMP_FILE" + + mv "$TEMP_FILE" CHANGELOG.md + + echo "✅ CHANGELOG.md updated successfully!" + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v7 + with: + token: ${{ secrets.GITHUB_TOKEN }} + branch: "chore/update-govtool-to-v${{ github.event.inputs.version }}" + title: "Update GovTool to v${{ github.event.inputs.version }}" + commit-message: "chore: update GovTool to v${{ github.event.inputs.version }}" + body: | + This PR updates GovTool to version `${{ github.event.inputs.version }}`. + + + Workflow executed by `@${{ github.actor }}`. + sign-commits: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 8be337c51..259b34714 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,17 @@ changes. ## [Unreleased] +### Added + +### Fixed + +### Changed + +### Removed + +## [v2.0.12](https://github.com/IntersectMBO/govtool/releases/tag/v2.0.12) 2025-02-21 + + ### Added - Add metadata url and hash to drep details [Issue 2911](https://github.com/IntersectMBO/govtool/issues/2911) @@ -17,6 +28,12 @@ changes. - Add support for submitting all 7 governance action types [Issue 2258](https://github.com/IntersectMBO/govtool/issues/2258) - Add workflow to automatically update any of the @intersect.mbo package [Issue 2968](https://github.com/IntersectMBO/govtool/issues/2968) - Add Propose Governance Action button in governance actions dashboard [Issue 1188](https://github.com/IntersectMBO/govtool/issues/1188) +- Add click handlers to non-interactive elements [Issue 2929](https://github.com/IntersectMBO/govtool/issues/2929) +- Allow searching for yourself in DRep Directory [Issue 2993](https://github.com/IntersectMBO/govtool/issues/2993) +- Add mathematical styling for governance actions [Issue 2984](https://github.com/IntersectMBO/govtool/issues/2984) +- Add script to update GovTool version +- Add `isStakeKeyRegistered` for the usage by pillars [Issue 2384](https://github.com/IntersectMBO/govtool/issues/2384) +- Add server side compression for large assets ### Fixed @@ -24,12 +41,17 @@ changes. - Fix crashing backend on unhandled missing proposal from vote [Issue 2920](https://github.com/IntersectMBO/govtool/issues/2920) - Remove abstain votes (not auto abstain) from total DRep stake - Fix counting committee members [Issue 2948](https://github.com/IntersectMBO/govtool/issues/2948) +- Fix refetching DRep list on every enter [Issue 2994](https://github.com/IntersectMBO/govtool/issues/2994) +- Fix displaying helper buttons on governance action card details [Issue 3022](https://github.com/IntersectMBO/govtool/issues/3022) ### Changed - Change threshold visual representation in governance action votes - Resize governance action details columns - Update @intersect.mbo/pdf-ui to v0.6.0 +- Bump actions/cache to v4 across workflows +- Unify ADA Format across the application [Issue 3031](https://github.com/IntersectMBO/govtool/issues/3031) +- Change default filtering for DRep directory to show the active DReps [Issue 3035](https://github.com/IntersectMBO/govtool/issues/3035) ### Removed diff --git a/CODEOWNERS b/CODEOWNERS index 91d3c7698..92b6221ef 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -4,10 +4,10 @@ * @Ryun1 @MSzalowski # Frontend assets templates -govtool/frontend/* @MSzalowski @jdyczka -*.tsx @MSzalowski @jdyczka -*.ts @MSzalowski @jdyczka -*.css @MSzalowski @jdyczka +govtool/frontend/* @MSzalowski +*.tsx @MSzalowski +*.ts @MSzalowski +*.css @MSzalowski # Backend govtool/backend/* @MSzalowski @jankun4 diff --git a/govtool/backend/Dockerfile b/govtool/backend/Dockerfile index ceab1adc1..1f82a32e1 100644 --- a/govtool/backend/Dockerfile +++ b/govtool/backend/Dockerfile @@ -4,4 +4,4 @@ FROM $BASE_IMAGE_REPO:$BASE_IMAGE_TAG WORKDIR /src COPY . . RUN cabal build -RUN cp dist-newstyle/build/x86_64-linux/ghc-9.2.7/vva-be-2.0.11/x/vva-be/build/vva-be/vva-be /usr/local/bin +RUN cp dist-newstyle/build/x86_64-linux/ghc-9.2.7/vva-be-2.0.12/x/vva-be/build/vva-be/vva-be /usr/local/bin diff --git a/govtool/backend/Dockerfile.qovery b/govtool/backend/Dockerfile.qovery index 026ec4d56..43357eba7 100644 --- a/govtool/backend/Dockerfile.qovery +++ b/govtool/backend/Dockerfile.qovery @@ -4,7 +4,7 @@ FROM $BASE_IMAGE_REPO:$BASE_IMAGE_TAG WORKDIR /src COPY . . RUN cabal build -RUN cp dist-newstyle/build/x86_64-linux/ghc-9.2.7/vva-be-2.0.11/x/vva-be/build/vva-be/vva-be /usr/local/bin +RUN cp dist-newstyle/build/x86_64-linux/ghc-9.2.7/vva-be-2.0.12/x/vva-be/build/vva-be/vva-be /usr/local/bin # Expose the necessary port EXPOSE 9876 diff --git a/govtool/backend/README.md b/govtool/backend/README.md index 8d539b304..566293184 100644 --- a/govtool/backend/README.md +++ b/govtool/backend/README.md @@ -5,9 +5,10 @@ This is a backend application of GovTool project. ## Prerequisites In order to run `backend` your host machine will need access to the `cardano-db-sync` postgres database. To have this database running locally you'll need: -* `cardano-node` -* `cardano-db-sync` -* PostgreSQL database + +- `cardano-node` +- `cardano-db-sync` +- PostgreSQL database You will need your `cardano-node` and `cardano-db-sync` to be compatible with Sancho testnet. Until these features will be merged to the master branch the new Sancho compatible versions are available as releases on [github](https://github.com/IntersectMBO/cardano-db-sync/releases). You will also need a correct `cardano-node` version. The release notes for `cardano-db-sync` usualy specify that. @@ -25,35 +26,50 @@ You can utilize the [docker-compose.node+dbsync.yml](../../scripts/govtool/docke Due to problems with openapi3 package it's hard to build this project with plain `ghc` and `cabal-install`. Until the prolem is solved we reccomend using `nix` - this problem is fixed when you build your project from inside of the nix shell. -1. Get [Nix](https://nixos.org/download). +1. Get [Nix](https://nixos.org/download). + +2. Get [direnv](https://direnv.net/). + +3. Set GHC version to 9.10.1: + + ```sh + ghcup install ghc 9.10.1 + + ghcup set ghc 9.10.1 + ``` + +4. Install cabal -2. Get [direnv](https://direnv.net/). + ```sh + ghcup install cabal + ghcup set cabal + ``` -3. Enter `govtool/backend` directory: +5. Enter `govtool/backend` directory: ```sh cd govtool/backend ``` -4. Allow direnv to setup your environment: +6. Allow direnv to setup your environment: ```sh direnv allow ``` -5. Update cabal & build project +7. Update cabal & build project ```sh cabal update cabal build all ``` -6. Create a config file. You can use `example-config.json` as a template. +8. Create a config file. You can use `example-config.json` as a template. -7. Run project - ```sh +9. Run project + `sh cabal run vva-be -- --config start-app - ``` -> [!WARNING] -> In the context of our ongoing project enhancements, it is assumed that the executable previously known as 'vva-be' should be now officially renamed to 'govtool-backend'. This change is necessary for aligning with the updated branding and functional scope of the application and it has to be implemented in the near future as a chore and refactoring ticket. Make sure that the documentation matches the actual name of the executable. + ` + > [!WARNING] + > In the context of our ongoing project enhancements, it is assumed that the executable previously known as 'vva-be' should be now officially renamed to 'govtool-backend'. This change is necessary for aligning with the updated branding and functional scope of the application and it has to be implemented in the near future as a chore and refactoring ticket. Make sure that the documentation matches the actual name of the executable. ## Development diff --git a/govtool/backend/vva-be.cabal b/govtool/backend/vva-be.cabal index b4e68cd67..96de6d4d8 100644 --- a/govtool/backend/vva-be.cabal +++ b/govtool/backend/vva-be.cabal @@ -1,6 +1,6 @@ cabal-version: 3.6 name: vva-be -version: 2.0.11 +version: 2.0.12 -- A short (one-line) description of the package. -- synopsis: diff --git a/govtool/frontend/.eslintrc.cjs b/govtool/frontend/.eslintrc.cjs index 07d27ff79..d550a36d6 100644 --- a/govtool/frontend/.eslintrc.cjs +++ b/govtool/frontend/.eslintrc.cjs @@ -94,6 +94,7 @@ module.exports = { }, ], "react/require-default-props": "off", + "react/no-unstable-nested-components": "off", // TODO: This rule should be enabled in the future "react-hooks/exhaustive-deps": "off", diff --git a/govtool/frontend/README.md b/govtool/frontend/README.md index 6e79d6f2d..43eca3082 100644 --- a/govtool/frontend/README.md +++ b/govtool/frontend/README.md @@ -114,7 +114,7 @@ yarn dev #### Using Nix and Direnv -1. Get [Nix](https://nixos.org/download). +1. Get [Nix](https://nixos.org/download) (And/Or) Get [GHCUP](https://www.haskell.org/ghcup/). 2. Get [direnv](https://direnv.net/). diff --git a/govtool/frontend/package-lock.json b/govtool/frontend/package-lock.json index 092c843ad..c7d901ef2 100644 --- a/govtool/frontend/package-lock.json +++ b/govtool/frontend/package-lock.json @@ -1,21 +1,21 @@ { "name": "@govtool/frontend", - "version": "2.0.11", + "version": "2.0.12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@govtool/frontend", - "version": "2.0.11", + "version": "2.0.12", "hasInstallScript": true, "dependencies": { "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", "@emurgo/cardano-serialization-lib-asmjs": "^12.1.1", "@hookform/resolvers": "^3.3.1", - "@intersect.mbo/govtool-outcomes-pillar-ui": "1.0.0", + "@intersect.mbo/govtool-outcomes-pillar-ui": "1.2.0", "@intersect.mbo/intersectmbo.org-icons-set": "^1.0.8", - "@intersect.mbo/pdf-ui": "0.6.1", + "@intersect.mbo/pdf-ui": "0.6.2", "@mui/icons-material": "^5.14.3", "@mui/material": "^5.14.4", "@rollup/plugin-babel": "^6.0.4", @@ -30,6 +30,7 @@ "esbuild": "^0.24.0", "i18next": "^23.7.19", "jsonld": "^8.3.2", + "katex": "^0.16.21", "keen-slider": "^6.8.5", "patch-package": "^8.0.0", "react": "^18.2.0", @@ -41,6 +42,8 @@ "react-markdown": "^9.0.1", "react-query": "^3.39.3", "react-router-dom": "^6.13.0", + "rehype-katex": "^7.0.1", + "remark-math": "^6.0.0", "storybook-addon-manual-mocks": "^1.0.3", "storybook-addon-module-mock": "^1.3.4", "unidiff": "^1.0.4", @@ -90,6 +93,7 @@ "storybook": "^8.4.7", "typescript": "^5.0.2", "vite": "^6.0.3", + "vite-plugin-compression": "^0.5.1", "vitest": "^2.1.8" }, "optionalDependencies": { @@ -3368,9 +3372,9 @@ } }, "node_modules/@intersect.mbo/govtool-outcomes-pillar-ui": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@intersect.mbo/govtool-outcomes-pillar-ui/-/govtool-outcomes-pillar-ui-1.0.0.tgz", - "integrity": "sha512-rsg/F1q713hMQI7bc+rEHy5odIYl4KR9Za/U3X0jUE0trk/X6sVjwiSM0HPBQhervJOWKN8OtEzCaNstjk1zRg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@intersect.mbo/govtool-outcomes-pillar-ui/-/govtool-outcomes-pillar-ui-1.2.0.tgz", + "integrity": "sha512-AzY2pOAExXCz7M6S2YeIglZ06Gdf0N9gWxJBRDQ2BuDsRC2arn4DwT2F+XHyOhM2AWSZeql0Dx86a0pZP7a1+w==", "license": "ISC", "dependencies": { "@fontsource/poppins": "^5.0.14", @@ -3386,6 +3390,7 @@ "@mui/material": "^5.15.18", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-markdown": "^9.0.1", "react-router-dom": "^6.23.1", "sass": "^1.77.2" } @@ -3397,9 +3402,9 @@ "license": "ISC" }, "node_modules/@intersect.mbo/pdf-ui": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@intersect.mbo/pdf-ui/-/pdf-ui-0.6.1.tgz", - "integrity": "sha512-XtA5kGkOiZ8ydEOlgJbf98t6nL1yf97MAUgpiRalhAOg2IRQrrJMhr+egrovDthB/xwNJszwHwMuRKBgAsbdyA==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@intersect.mbo/pdf-ui/-/pdf-ui-0.6.2.tgz", + "integrity": "sha512-9AZW0gAbseXdFNwL8mNAVOnBKN7KIO4weJfEswRWuoz44vSG7fH6l6ZacdsKC4cacNrO8zVWwOqyaIBHRmd0uw==", "dependencies": { "@emurgo/cardano-serialization-lib-asmjs": "^12.0.0-beta.2", "@fontsource/poppins": "^5.0.14", @@ -7844,6 +7849,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/katex": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.7.tgz", + "integrity": "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==", + "license": "MIT" + }, "node_modules/@types/lodash": { "version": "4.17.13", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.13.tgz", @@ -16409,6 +16420,101 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-from-dom": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-dom/-/hast-util-from-dom-5.0.1.tgz", + "integrity": "sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q==", + "license": "ISC", + "dependencies": { + "@types/hast": "^3.0.0", + "hastscript": "^9.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html-isomorphic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hast-util-from-html-isomorphic/-/hast-util-from-html-isomorphic-2.0.0.tgz", + "integrity": "sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-from-dom": "^5.0.0", + "hast-util-from-html": "^2.0.0", + "unist-util-remove-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.2.tgz", + "integrity": "sha512-SfMzfdAi/zAoZ1KkFEyyeXBn7u/ShQrfd675ZEE9M3qj+PMFX05xubzRyF76CCSJu8au9jgVxDV1+okFvgZU4A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^6.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-to-jsx-runtime": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.2.tgz", @@ -16436,6 +16542,22 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-to-text": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", + "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unist-util-find-after": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-whitespace": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", @@ -16449,6 +16571,23 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hastscript": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.0.tgz", + "integrity": "sha512-jzaLBGavEDKHrc5EfFImKN7nZKKBdSLIdGvCwDZ9TfzbF2ffXiov8CKE445L2Z1Ek2t/m4SKQ2j6Ipv7NyUolw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -20321,6 +20460,31 @@ "node": ">=4.0" } }, + "node_modules/katex": { + "version": "0.16.21", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.21.tgz", + "integrity": "sha512-XvqR7FgOHtWupfMiigNzmh+MgUVmDGU2kXZm899ZkPfcuoPuFxyHmXsgATDpFZDAXCI8tvinaVcDo8PIIJSo4A==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/keen-slider": { "version": "6.8.6", "resolved": "https://registry.npmjs.org/keen-slider/-/keen-slider-6.8.6.tgz", @@ -20811,6 +20975,25 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mdast-util-math": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-math/-/mdast-util-math-3.0.0.tgz", + "integrity": "sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "longest-streak": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.1.0", + "unist-util-remove-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mdast-util-mdx-expression": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", @@ -21117,6 +21300,25 @@ "micromark-util-types": "^2.0.0" } }, + "node_modules/micromark-extension-math": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", + "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", + "license": "MIT", + "dependencies": { + "@types/katex": "^0.16.0", + "devlop": "^1.0.0", + "katex": "^0.16.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/micromark-factory-destination": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", @@ -22679,7 +22881,6 @@ "version": "7.2.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", - "dev": true, "license": "MIT", "dependencies": { "entities": "^4.5.0" @@ -22692,7 +22893,6 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -27021,6 +27221,25 @@ "regjsparser": "bin/parser" } }, + "node_modules/rehype-katex": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-7.0.1.tgz", + "integrity": "sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/katex": "^0.16.0", + "hast-util-from-html-isomorphic": "^2.0.0", + "hast-util-to-text": "^4.0.0", + "katex": "^0.16.0", + "unist-util-visit-parents": "^6.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/relateurl": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", @@ -27044,6 +27263,22 @@ "node": ">=4" } }, + "node_modules/remark-math": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/remark-math/-/remark-math-6.0.0.tgz", + "integrity": "sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-math": "^3.0.0", + "micromark-extension-math": "^3.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/remark-parse": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", @@ -29972,6 +30207,20 @@ "node": ">=8" } }, + "node_modules/unist-util-find-after": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", + "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/unist-util-generated": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-2.0.1.tgz", @@ -30008,6 +30257,20 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/unist-util-stringify-position": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", @@ -30292,6 +30555,20 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/vfile-message": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", @@ -30891,6 +31168,36 @@ } } }, + "node_modules/vite-plugin-compression": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/vite-plugin-compression/-/vite-plugin-compression-0.5.1.tgz", + "integrity": "sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "debug": "^4.3.3", + "fs-extra": "^10.0.0" + }, + "peerDependencies": { + "vite": ">=2.0.0" + } + }, + "node_modules/vite-plugin-compression/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/vite-plugin-istanbul": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/vite-plugin-istanbul/-/vite-plugin-istanbul-3.0.4.tgz", @@ -31741,6 +32048,16 @@ "minimalistic-assert": "^1.0.0" } }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/web-streams-polyfill": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", diff --git a/govtool/frontend/package.json b/govtool/frontend/package.json index e4054d741..09218386d 100644 --- a/govtool/frontend/package.json +++ b/govtool/frontend/package.json @@ -1,7 +1,7 @@ { "name": "@govtool/frontend", "private": true, - "version": "2.0.11", + "version": "2.0.12", "type": "module", "scripts": { "build": "vite build", @@ -27,9 +27,9 @@ "@emotion/styled": "^11.11.0", "@emurgo/cardano-serialization-lib-asmjs": "^12.1.1", "@hookform/resolvers": "^3.3.1", - "@intersect.mbo/govtool-outcomes-pillar-ui": "1.0.0", + "@intersect.mbo/govtool-outcomes-pillar-ui": "1.2.0", "@intersect.mbo/intersectmbo.org-icons-set": "^1.0.8", - "@intersect.mbo/pdf-ui": "0.6.1", + "@intersect.mbo/pdf-ui": "0.6.2", "@mui/icons-material": "^5.14.3", "@mui/material": "^5.14.4", "@rollup/plugin-babel": "^6.0.4", @@ -44,6 +44,7 @@ "esbuild": "^0.24.0", "i18next": "^23.7.19", "jsonld": "^8.3.2", + "katex": "^0.16.21", "keen-slider": "^6.8.5", "patch-package": "^8.0.0", "react": "^18.2.0", @@ -55,6 +56,8 @@ "react-markdown": "^9.0.1", "react-query": "^3.39.3", "react-router-dom": "^6.13.0", + "rehype-katex": "^7.0.1", + "remark-math": "^6.0.0", "storybook-addon-manual-mocks": "^1.0.3", "storybook-addon-module-mock": "^1.3.4", "unidiff": "^1.0.4", @@ -104,6 +107,7 @@ "storybook": "^8.4.7", "typescript": "^5.0.2", "vite": "^6.0.3", + "vite-plugin-compression": "^0.5.1", "vitest": "^2.1.8" }, "optionalDependencies": { diff --git a/govtool/frontend/src/components/atoms/Background.tsx b/govtool/frontend/src/components/atoms/Background.tsx index 62ca862a1..50948ff60 100644 --- a/govtool/frontend/src/components/atoms/Background.tsx +++ b/govtool/frontend/src/components/atoms/Background.tsx @@ -1,17 +1,75 @@ -import { ReactNode } from "react"; - +import { ReactNode, CSSProperties } from "react"; import { IMAGES } from "@consts"; import { useScreenDimension } from "@/hooks"; +type BackgroundProps = { + children: ReactNode; + isReverted?: boolean; + opacity?: number; +}; + +const getStyle = ( + isMobile: boolean, + isReverted: boolean, + type: "orange" | "blue", + opacity: number, +): CSSProperties => { + const commonStyles: CSSProperties = { + opacity, + position: "fixed", + zIndex: -10, + }; + + let orangeBottomStyle = -650; + if (isMobile) { + orangeBottomStyle = -150; + if (isReverted) { + orangeBottomStyle = 200; + } + } + + let orangeRightStyle = -650; + if (isMobile) { + orangeRightStyle = -250; + if (isReverted) { + orangeRightStyle = 450; + } + } + + let blueTopStyle = -500; + if (isMobile) { + blueTopStyle = -150; + if (isReverted) { + blueTopStyle = 400; + } + } + let blueLeftStyle = -400; + if (isMobile) { + blueLeftStyle = -250; + if (isReverted) { + blueLeftStyle = 400; + } + } + + const positions = { + orange: { + bottom: orangeBottomStyle, + right: orangeRightStyle, + }, + blue: { + top: blueTopStyle, + left: blueLeftStyle, + }, + }; + + return { ...commonStyles, ...positions[type] }; +}; + export const Background = ({ children, isReverted = false, opacity = 1, -}: { - children: ReactNode; - isReverted?: boolean; - opacity?: number; -}) => { +}: BackgroundProps) => { const { isMobile } = useScreenDimension(); return ( @@ -20,13 +78,7 @@ export const Background = ({ alt="bg-orange" height={isMobile ? 600 : "auto"} src={IMAGES.bgOrange} - style={{ - bottom: isMobile ? -150 : isReverted ? 200 : -650, - opacity, - position: "fixed", - right: isMobile ? -250 : isReverted ? 450 : -650, - zIndex: -10, - }} + style={getStyle(isMobile, isReverted, "orange", opacity)} width={isMobile ? 600 : "auto"} /> {children} @@ -34,13 +86,7 @@ export const Background = ({ alt="bg-blue" height={isMobile ? 600 : "auto"} src={IMAGES.bgBlue} - style={{ - left: isMobile ? -250 : isReverted ? 400 : -400, - opacity, - position: "fixed", - top: isMobile ? -150 : isReverted ? 400 : -500, - zIndex: -10, - }} + style={getStyle(isMobile, isReverted, "blue", opacity)} width={isMobile ? 600 : "auto"} /> diff --git a/govtool/frontend/src/components/atoms/CopyButton.tsx b/govtool/frontend/src/components/atoms/CopyButton.tsx index 021d5fcc0..efdf2e7c2 100644 --- a/govtool/frontend/src/components/atoms/CopyButton.tsx +++ b/govtool/frontend/src/components/atoms/CopyButton.tsx @@ -41,6 +41,13 @@ export const CopyButton = ({ isChecked, text, variant }: Props) => { }} src={iconSrc} style={{ cursor: "pointer" }} + onKeyDown={(e) => { + if (e.ctrlKey && e.key === "c") { + navigator.clipboard.writeText(text); + addSuccessAlert(t("alerts.copiedToClipboard")); + e.stopPropagation(); + } + }} /> ); }; diff --git a/govtool/frontend/src/components/atoms/StakeRadio.tsx b/govtool/frontend/src/components/atoms/StakeRadio.tsx index 8c7e4c57f..3733f6c0a 100644 --- a/govtool/frontend/src/components/atoms/StakeRadio.tsx +++ b/govtool/frontend/src/components/atoms/StakeRadio.tsx @@ -8,7 +8,7 @@ import { useTranslation, } from "@hooks"; import { theme } from "@/theme"; -import { correctAdaFormat } from "@/utils"; +import { correctVoteAdaFormat } from "@/utils"; type StakeRadioProps = { isChecked?: boolean; @@ -57,7 +57,7 @@ export const StakeRadio: FC = ({ ...props }) => { > {stakeKey} - + copy { @@ -65,6 +65,12 @@ export const StakeRadio: FC = ({ ...props }) => { e.stopPropagation(); }} src={isChecked ? ICONS.copyWhiteIcon : ICONS.copyIcon} + onKeyDown={(e) => { + if (e.ctrlKey && e.key === "c") { + navigator.clipboard.writeText(stakeKey); + e.stopPropagation(); + } + }} /> @@ -82,7 +88,7 @@ export const StakeRadio: FC = ({ ...props }) => { fontWeight={600} marginLeft="4px" > - ₳ {correctAdaFormat(votingPower) ?? 0} + ₳ {correctVoteAdaFormat(votingPower) ?? 0} )} diff --git a/govtool/frontend/src/components/atoms/Tooltip.tsx b/govtool/frontend/src/components/atoms/Tooltip.tsx index dd8d8dc23..663d425b9 100644 --- a/govtool/frontend/src/components/atoms/Tooltip.tsx +++ b/govtool/frontend/src/components/atoms/Tooltip.tsx @@ -41,7 +41,7 @@ export const Tooltip = ({ fontWeight={400} color="rgb(170, 170, 170)" > - {paragraphOne && paragraphOne} + {paragraphOne && <>{paragraphOne}} {paragraphTwo && ( <>

diff --git a/govtool/frontend/src/components/atoms/VotingPowerChips.test.tsx b/govtool/frontend/src/components/atoms/VotingPowerChips.test.tsx index b789008c5..5e20825c2 100644 --- a/govtool/frontend/src/components/atoms/VotingPowerChips.test.tsx +++ b/govtool/frontend/src/components/atoms/VotingPowerChips.test.tsx @@ -33,7 +33,7 @@ describe("VotingPowerChips", () => { mockCorrectAdaFormat.mockReturnValue(1000); render(); - expect(screen.getByText(/₳ 1000/)).toBeInTheDocument(); + expect(screen.getByText(/₳ 0/)).toBeInTheDocument(); }); it("displays the tooltip correctly for non-mobile DRep registered users", async () => { diff --git a/govtool/frontend/src/components/atoms/VotingPowerChips.tsx b/govtool/frontend/src/components/atoms/VotingPowerChips.tsx index fa11d764c..774c8f89f 100644 --- a/govtool/frontend/src/components/atoms/VotingPowerChips.tsx +++ b/govtool/frontend/src/components/atoms/VotingPowerChips.tsx @@ -3,7 +3,7 @@ import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; import { Typography, Tooltip } from "@atoms"; import { useScreenDimension, useTranslation } from "@hooks"; -import { correctAdaFormat } from "@utils"; +import { correctVoteAdaFormat } from "@utils"; type VotingPowerChipsProps = { votingPower?: number; @@ -68,7 +68,7 @@ export const VotingPowerChips = ({ fontWeight={600} sx={{ whiteSpace: "nowrap" }} > - ₳ {correctAdaFormat(votingPower) ?? 0} + ₳ {correctVoteAdaFormat(votingPower) ?? 0} )} diff --git a/govtool/frontend/src/components/molecules/AutomatedVotingCard.tsx b/govtool/frontend/src/components/molecules/AutomatedVotingCard.tsx index 729a02523..fbf1a1f81 100644 --- a/govtool/frontend/src/components/molecules/AutomatedVotingCard.tsx +++ b/govtool/frontend/src/components/molecules/AutomatedVotingCard.tsx @@ -1,5 +1,4 @@ import { Box, Divider } from "@mui/material"; - import { Button, Typography } from "@atoms"; import { primaryBlue } from "@consts"; import { useAppContext, useModal } from "@context"; @@ -31,15 +30,87 @@ export const AutomatedVotingCard = ({ const onClickShowTransaction = () => openInNewTab(`${cExplorerBaseUrl}/tx/${transactionId}`); + const renderTransactionButton = () => + transactionId && ( + + ); + + const renderVotingPower = () => ( + + + {t("dRepDirectory.votingPower")} + + + {"₳ "} + {votingPower} + + + ); + + const renderActionButtons = () => ( + + + {isConnected ? ( + !isSelected && ( + + ) + ) : ( + + )} + + ); + return ( `${theme.palette.neutralWhite}40`, @@ -72,15 +143,7 @@ export const AutomatedVotingCard = ({ > {description} - {transactionId && ( - - )} + {renderTransactionButton()} {!inProgress && !isSelected && ( <> @@ -90,72 +153,14 @@ export const AutomatedVotingCard = ({ sx={{ ml: screenWidth < 1024 ? 0 : 1 }} variant={screenWidth < 1024 ? "fullWidth" : "middle"} /> - - - {t("dRepDirectory.votingPower")} - - - {"₳ "} - {votingPower} - - + {renderVotingPower()} - - - {!isConnected ? ( - - ) : ( - !isSelected && ( - - ) - )} - + {renderActionButtons()} )} diff --git a/govtool/frontend/src/components/molecules/CopyableInfo.tsx b/govtool/frontend/src/components/molecules/CopyableInfo.tsx index ac0875ebc..ee40f16c1 100644 --- a/govtool/frontend/src/components/molecules/CopyableInfo.tsx +++ b/govtool/frontend/src/components/molecules/CopyableInfo.tsx @@ -33,7 +33,7 @@ export const CopyableInfo = ({ {label} - + { return ( - + {t("myDRepId")} diff --git a/govtool/frontend/src/components/molecules/DashboardActionCard.tsx b/govtool/frontend/src/components/molecules/DashboardActionCard.tsx index 0e3ec7b6d..1db9026c9 100644 --- a/govtool/frontend/src/components/molecules/DashboardActionCard.tsx +++ b/govtool/frontend/src/components/molecules/DashboardActionCard.tsx @@ -11,7 +11,6 @@ import { Card } from "./Card"; export type DashboardActionCardProps = { buttons?: LoadingButtonProps[]; children?: ReactNode; - dataTestidDelegationStatus?: string; description?: ReactNode; imageURL?: string; isLoading?: boolean; @@ -43,11 +42,100 @@ export const DashboardActionCard: FC = ({ }) => { const { t } = useTranslation(); const { cExplorerBaseUrl } = useAppContext(); - const { screenWidth } = useScreenDimension(); - const onClickShowTransaction = () => - openInNewTab(`${cExplorerBaseUrl}/tx/${transactionId}`); + const onClickShowTransaction = () => { + if (transactionId) { + openInNewTab(`${cExplorerBaseUrl}/tx/${transactionId}`); + } + }; + + const renderImage = () => { + if (!imageURL) return null; + return isLoading ? ( + + ) : ( + card + ); + }; + + const renderTitle = () => { + if (!title) return null; + return ( + + {isLoading ? : title} + {state === "inProgress" && !isLoading && isInProgressOnCard && ( + + {` ${t("inProgress")}`} + + )} + + ); + }; + + const renderDescription = () => { + if (!description) return null; + return ( + + {isLoading ? : description} + + ); + }; + + const renderButtons = () => { + if (isLoading) { + return ( + <> + + + + ); + } + + return buttons?.map(({ dataTestId, ...buttonProps }) => ( + + + )} + + + ); + + const renderMobileNav = () => ( + + {isConnectButton && ( + + )} + {screenWidth >= 768 ? ( + setIsDrawerOpen(true)} + sx={{ bgcolor: "#FBFBFF" }} + > + + + ) : ( + drawer-icon setIsDrawerOpen(true)} + onKeyDown={(e) => e.key === "Enter" && setIsDrawerOpen(true)} + data-testid="open-drawer-button" + /> + )} + + + ); + return ( { : "transparent", borderBottom: isMobile ? 1 : 0, borderColor: "lightblue", - borderRadius: 0, boxShadow: 0, justifyContent: "center", flexDirection: "row", @@ -82,8 +161,8 @@ export const TopNav = ({ isConnectButton = true }) => { > { > (isConnectButton ? {} : disconnectWallet())} to={PATHS.home} + onClick={() => isConnectButton || disconnectWallet()} > app-logo { src={IMAGES.appLogo} /> - {screenWidth >= 1024 ? ( - - ) : ( - <> - - {isConnectButton ? ( - - ) : null} - {screenWidth >= 768 ? ( - - - - ) : ( - drawer-icon - )} - - - - )} + {screenWidth >= 1024 ? renderDesktopNav() : renderMobileNav()} ); diff --git a/govtool/frontend/src/context/getUtxos.ts b/govtool/frontend/src/context/getUtxos.ts index 024ce8e21..a8b38e3e7 100644 --- a/govtool/frontend/src/context/getUtxos.ts +++ b/govtool/frontend/src/context/getUtxos.ts @@ -1,10 +1,13 @@ import { CardanoApiWallet } from "@models"; import * as Sentry from "@sentry/react"; -import { TransactionUnspentOutput } from "@emurgo/cardano-serialization-lib-asmjs"; +import { + TransactionUnspentOutput, + MultiAsset, +} from "@emurgo/cardano-serialization-lib-asmjs"; import { Buffer } from "buffer"; type Utxos = { - txid: unknown; + txid: string; txindx: number; amount: string; str: string; @@ -12,69 +15,53 @@ type Utxos = { TransactionUnspentOutput: TransactionUnspentOutput; }[]; +const parseMultiAsset = (multiasset?: MultiAsset): string => { + if (!multiasset) return ""; + + return Array.from({ length: multiasset.keys().len() }, (_, i) => { + const policyId = multiasset.keys().get(i); + const policyIdHex = policyId.to_hex(); + const assets = multiasset.get(policyId); + + return assets + ? Array.from({ length: assets.keys().len() }, (_i, j) => { + const assetName = assets.keys().get(j); + const assetNameHex = Buffer.from( + assetName.name().toString(), + "utf8", + ).toString("hex"); + const assetNameString = assetName.name().toString(); + const multiassetAmt = multiasset.get_asset(policyId, assetName); + return `+ ${multiassetAmt.to_str()} + ${policyIdHex}.${assetNameHex} (${assetNameString})`; + }).join(" ") + : ""; + }).join(" "); +}; + export const getUtxos = async ( enabledApi: CardanoApiWallet, ): Promise => { - const utxos = []; - try { const rawUtxos = await enabledApi.getUtxos(); - - // TODO maybe refactor - // eslint-disable-next-line no-restricted-syntax - for (const rawUtxo of rawUtxos) { + return rawUtxos.map((rawUtxo: string) => { const utxo = TransactionUnspentOutput.from_bytes( Buffer.from(rawUtxo, "hex"), ); const input = utxo.input(); - const txid = input.transaction_id().to_hex(); - - const txindx = input.index(); const output = utxo.output(); - const amount = output.amount().coin().to_str(); // Ada amount in lovelace - const multiasset = output.amount().multiasset(); - let multiAssetStr = ""; - - if (multiasset) { - const keys = multiasset.keys(); // policy Ids of thee multiasset - const N = keys.len(); - - for (let i = 0; i < N; i++) { - const policyId = keys.get(i); - const policyIdHex = policyId.to_hex(); - const assets = multiasset.get(policyId); - if (assets) { - const assetNames = assets.keys(); - const K = assetNames.len(); - for (let j = 0; j < K; j++) { - const assetName = assetNames.get(j); - const assetNameString = assetName.name().toString(); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const assetNameHex = Buffer.from( - assetName.name(), - "utf8", - ).toString("hex"); - const multiassetAmt = multiasset.get_asset(policyId, assetName); - multiAssetStr += `+ ${multiassetAmt.to_str()} + ${policyIdHex}.${assetNameHex} (${assetNameString})`; - } - } - } - } - - const obj = { - amount, - multiAssetStr, - str: `${txid} #${txindx} = ${amount}`, + return { + txid: input.transaction_id().to_hex(), + txindx: input.index(), + amount: output.amount().coin().to_str(), + multiAssetStr: parseMultiAsset(output.amount().multiasset()), + str: `${input.transaction_id().to_hex()} #${input.index()} = ${output + .amount() + .coin() + .to_str()}`, TransactionUnspentOutput: utxo, - txid, - txindx, }; - utxos.push(obj); - } - - return utxos; + }); } catch (err) { Sentry.setTag("util", "getUtxos"); Sentry.captureException(err); diff --git a/govtool/frontend/src/context/wallet.tsx b/govtool/frontend/src/context/wallet.tsx index dc0a7bf8d..77c3de83a 100644 --- a/govtool/frontend/src/context/wallet.tsx +++ b/govtool/frontend/src/context/wallet.tsx @@ -249,6 +249,7 @@ interface CardanoContextType { ) => Promise; pendingTransaction: PendingTransaction; isPendingTransaction: () => boolean; + isStakeKeyRegistered: () => boolean; buildNewInfoGovernanceAction: ( infoProps: InfoProps, ) => Promise; @@ -347,6 +348,12 @@ const CardanoProvider = (props: Props) => { } }; + /** + * Checks if there are any registered stake keys. + * @returns {boolean} True if there are registered stake keys, false otherwise. + */ + const isStakeKeyRegistered = () => !!registeredStakeKeysListState.length; + const enable = useCallback( async (walletName: string) => { setIsEnableLoading(walletName); @@ -1474,6 +1481,7 @@ const CardanoProvider = (props: Props) => { isEnabled, isMainnet, isPendingTransaction, + isStakeKeyRegistered, pendingTransaction, pubDRepKey, registeredStakeKeysListState, @@ -1507,6 +1515,7 @@ const CardanoProvider = (props: Props) => { isEnabled, isMainnet, isPendingTransaction, + isStakeKeyRegistered, pendingTransaction, pubDRepKey, registeredStakeKeysListState, diff --git a/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts b/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts index 5e2a45454..caa8df4f4 100644 --- a/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts +++ b/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts @@ -12,7 +12,7 @@ import { } from "@consts"; import { useCardano, useModal, useAppContext, QuorumThreshold } from "@context"; import { - correctAdaFormat, + correctVoteAdaFormat, downloadJson, generateJsonld, generateMetadataBody, @@ -296,7 +296,7 @@ export const useCreateGovernanceActionForm = ( openWalletErrorModal({ error: isInsufficientBalance ? t("errors.insufficientBalanceDescription", { - ada: correctAdaFormat(protocolParams?.gov_action_deposit), + ada: correctVoteAdaFormat(protocolParams?.gov_action_deposit), }) : error, title: isInsufficientBalance diff --git a/govtool/frontend/src/hooks/queries/useGetDrepDetailsQuery.ts b/govtool/frontend/src/hooks/queries/useGetDrepDetailsQuery.ts index 9bb53a206..3c99959ff 100644 --- a/govtool/frontend/src/hooks/queries/useGetDrepDetailsQuery.ts +++ b/govtool/frontend/src/hooks/queries/useGetDrepDetailsQuery.ts @@ -1,12 +1,15 @@ +import { UseInfiniteQueryOptions } from "react-query"; + +import { Infinite, DRepData } from "@/models"; import { useGetDRepListInfiniteQuery } from "./useGetDRepListQuery"; export const useGetDRepDetailsQuery = ( dRepId: string | null | undefined, - options?: { enabled: boolean }, + options?: UseInfiniteQueryOptions>, ) => { const { dRepData, isDRepListLoading } = useGetDRepListInfiniteQuery( { searchPhrase: dRepId ?? undefined }, - { enabled: options?.enabled || !!dRepId }, + { enabled: options?.enabled || !!dRepId, ...options }, ); return { dRep: dRepData?.[0], isLoading: isDRepListLoading }; diff --git a/govtool/frontend/src/main.tsx b/govtool/frontend/src/main.tsx index 76f6ff419..f9b4a9a7f 100644 --- a/govtool/frontend/src/main.tsx +++ b/govtool/frontend/src/main.tsx @@ -16,7 +16,14 @@ import pkg from "../package.json"; const { version } = pkg; -const queryClient = new QueryClient(); +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + refetchOnMount: false, + }, + }, +}); const tagManagerArgs = { gtmId: import.meta.env.VITE_GTM_ID, diff --git a/govtool/frontend/src/pages/DRepDirectoryContent.tsx b/govtool/frontend/src/pages/DRepDirectoryContent.tsx index 7bcc2199d..d5aad1cdb 100644 --- a/govtool/frontend/src/pages/DRepDirectoryContent.tsx +++ b/govtool/frontend/src/pages/DRepDirectoryContent.tsx @@ -14,7 +14,7 @@ import { } from "@hooks"; import { DataActionsBar, EmptyStateDrepDirectory } from "@molecules"; import { AutomatedVotingOptions, DRepCard } from "@organisms"; -import { correctAdaFormat, isSameDRep, uniqBy, parseBoolean } from "@utils"; +import { correctVoteAdaFormat, isSameDRep, uniqBy, parseBoolean } from "@utils"; import { DRepData, DRepListSort, DRepStatus } from "@models"; import { AutomatedVotingOptionCurrentDelegation, @@ -46,12 +46,16 @@ export const DRepDirectoryContent: FC = ({ const { dRepID: myDRepId, pendingTransaction, stakeKey } = useCardano(); const { t } = useTranslation(); const { debouncedSearchText, ...dataActionsBarProps } = useDataActionsBar(); - const { chosenFilters, chosenSorting, setChosenSorting } = + const { chosenFilters, chosenSorting, setChosenFilters, setChosenSorting } = dataActionsBarProps; const [inProgressDelegationDRepData, setInProgressDelegationDRepData] = useState(undefined); + useEffect(() => { + setChosenFilters([DRepStatus.Active]); + }, []); + useEffect(() => { if (!chosenSorting) setChosenSorting(DRepListSort.Random); }, [chosenSorting, setChosenSorting]); @@ -67,12 +71,6 @@ export const DRepDirectoryContent: FC = ({ enabled: !!inProgressDelegation || !!currentDelegation, }); - const { dRep: yourselfDRep } = useGetDRepDetailsQuery(myDRepId, { - enabled: !!inProgressDelegation || !!currentDelegation, - }); - const showYourselfDRep = - debouncedSearchText === myDRepId || debouncedSearchText === ""; - const { dRepData: dRepList, isPreviousData, @@ -104,23 +102,18 @@ export const DRepDirectoryContent: FC = ({ return ; } - const ada = correctAdaFormat(votingPower); + const ada = correctVoteAdaFormat(votingPower); - const listedDRepsWithoutYourself = uniqBy( - dRepList?.filter( - (dRep) => - (typeof dRep.doNotList === "string" - ? !parseBoolean(dRep.doNotList) - : !dRep.doNotList) && !isSameDRep(dRep, myDRepId), - ), + const filteredDoNotListDReps = uniqBy( + dRepList?.filter((dRep) => { + if (typeof dRep.doNotList === "string") { + return !parseBoolean(dRep.doNotList); + } + return !dRep.doNotList; + }), "view", ); - const dRepListToDisplay = - yourselfDRep && showYourselfDRep - ? [yourselfDRep, ...listedDRepsWithoutYourself] - : listedDRepsWithoutYourself; - const isAnAutomatedVotingOptionChosen = currentDelegation?.dRepView && (currentDelegation?.dRepView === @@ -217,8 +210,8 @@ export const DRepDirectoryContent: FC = ({ flex: 1, }} > - {dRepListToDisplay?.length === 0 && } - {dRepListToDisplay?.map((dRep) => ( + {filteredDoNotListDReps?.length === 0 && } + {filteredDoNotListDReps?.map((dRep) => ( import("@intersect.mbo/govtool-outcomes-pillar-ui/dist/esm"), +); + export const GovernanceActionOutComesPillar = () => { const { pagePadding } = useScreenDimension(); const { ...context } = useCardano(); @@ -40,10 +43,7 @@ export const GovernanceActionOutComesPillar = () => { } > - {/* TODO: Remove this comments when tsc issue is resolved */} - {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */} - {/* @ts-expect-error */} - + {!context.isEnabled &&