diff --git a/scripts/govtool/.env.example b/.envrc.example similarity index 72% rename from scripts/govtool/.env.example rename to .envrc.example index c36704720..58f191c15 100644 --- a/scripts/govtool/.env.example +++ b/.envrc.example @@ -1,10 +1,6 @@ # vim: set ft=bash # This file is a template for the environment variables that need to be set and -# should be copied to `.env` or `.envrc` and filled in with the correct values. - -# Tell direnv to use Nix configuration. -# See https://direnv.net/docs/hook.html#nix. -use nix shell.nix +# should be copied to `.envrc` and filled in with the correct values. # target environment: dev, test, staging or beta export ENVIRONMENT= @@ -34,33 +30,25 @@ export GRAFANA_SLACK_RECIPIENT= export GRAFANA_SLACK_OAUTH_TOKEN= # the basic auth credentials for the Nginx proxy export NGINX_BASIC_AUTH= +# the IP addresses that bypass the basic auth +export IP_ADDRESS_BYPASSING_BASIC_AUTH1= +export IP_ADDRESS_BYPASSING_BASIC_AUTH2= - -# The following code is a nice way to display what environment we are in. -function warn() { - tput bold; tput setaf $2; echo "GovTool: $1"; tput sgr0 -} - -tput bold +# the domain name for the target environment case "$ENVIRONMENT" in "dev") export DOMAIN="dev-${CARDANO_NETWORK}.govtool.byron.network" - warn "This is a DEV environment, all good!" 2 ;; "test") export DOMAIN="test-${CARDANO_NETWORK}.govtool.byron.network" - warn "This is TEST environment, be careful…" 4 ;; "staging") export DOMAIN="staging.govtool.byron.network" - warn "This is STAGING environment, be careful…" 5 ;; "beta") export DOMAIN="sanchogov.tools" - warn "This is BETA environment, BE CAREFUL!" 1 ;; *) - warn "Unknown environment, exiting…" 1 exit 1 ;; esac diff --git a/.github/workflows/code_check_frontend.yml b/.github/workflows/code_check_frontend.yml index eaa943dae..d9ac0bc1b 100644 --- a/.github/workflows/code_check_frontend.yml +++ b/.github/workflows/code_check_frontend.yml @@ -29,6 +29,8 @@ jobs: node-version-file: "govtool/frontend/.nvmrc" - name: 🧪 Test + env: + NODE_OPTIONS: "--max_old_space_size=4096" run: | npm install npm run test @@ -51,7 +53,7 @@ jobs: node-version-file: "govtool/frontend/.nvmrc" - name: 👕 Lint - run: / + run: | npm install npm run lint @@ -73,6 +75,6 @@ jobs: node-version-file: "govtool/frontend/.nvmrc" - name: 🔍 Type Check - run: / + run: | npm install npm run tsc diff --git a/.github/workflows/deploy-dev-from-tag.yml b/.github/workflows/deploy-dev-from-tag.yml new file mode 100644 index 000000000..df9b4ba7f --- /dev/null +++ b/.github/workflows/deploy-dev-from-tag.yml @@ -0,0 +1,66 @@ +name: Build and deploy GovTool to DEV server +run-name: Deploy by @${{ github.actor }} + +on: + create: + +env: + ENVIRONMENT: "dev" + CARDANO_NETWORK: "sanchonet" + DOMAIN: "dev-sanchonet.govtool.byron.network" + +jobs: + deploy: + name: Deploy app + if: github.ref_type == 'tag' && startsWith(github.ref, 'refs/tags/dev-') + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./scripts/govtool + env: + DBSYNC_POSTGRES_DB: "cexplorer" + DBSYNC_POSTGRES_USER: "postgres" + DBSYNC_POSTGRES_PASSWORD: "pSa8JCpQOACMUdGb" + GRAFANA_ADMIN_PASSWORD: ${{ secrets.GRAFANA_ADMIN_PASSWORD }} + GRAFANA_SLACK_RECIPIENT: ${{ secrets.GRAFANA_SLACK_RECIPIENT }} + GRAFANA_SLACK_OAUTH_TOKEN: ${{ secrets.GRAFANA_SLACK_OAUTH_TOKEN }} + NGINX_BASIC_AUTH: ${{ secrets.NGINX_BASIC_AUTH }} + SENTRY_DSN_BACKEND: ${{ secrets.SENTRY_DSN_BACKEND }} + TRAEFIK_LE_EMAIL: "admin+govtool@binarapps.com" + GTM_ID: ${{ secrets.GTM_ID }} + SENTRY_DSN: ${{ secrets.SENTRY_DSN_FRONTEND }} + PIPELINE_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} + IP_ADDRESS_BYPASSING_BASIC_AUTH1: ${{ secrets.IP_ADDRESS_BYPASSING_BASIC_AUTH1 }} + IP_ADDRESS_BYPASSING_BASIC_AUTH2: ${{ secrets.IP_ADDRESS_BYPASSING_BASIC_AUTH2 }} + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + ref: ${{ github.ref }} + fetch-depth: 0 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + aws-access-key-id: ${{ secrets.GHA_AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.GHA_AWS_SECRET_ACCESS_KEY }} + aws-region: eu-west-1 + + - name: Login to AWS ECR + uses: aws-actions/configure-aws-credentials@v2 + with: + aws-region: eu-west-1 + + - name: Setup SSH agent + uses: webfactory/ssh-agent@v0.8.0 + with: + ssh-private-key: ${{ secrets.GHA_SSH_PRIVATE_KEY }} + + - name: Deploy app + run: | + make --debug=b all + + - name: Reprovision Grafana + run: | + sleep 30 # give grafana time to start up + make --debug=b reload-grafana diff --git a/.gitignore b/.gitignore index 2b28f6824..60ee619a1 100644 --- a/.gitignore +++ b/.gitignore @@ -100,7 +100,7 @@ book/src/08_event-db/db-diagrams/*.dot # Used by nix result* -/.direnv/ +.direnv/ /.pre-commit-config.yaml # Development Environments diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cc84ea7f..f9fea525b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ As a minor extension, we also keep a semantic version for the `UNRELEASED` changes. ## [Unreleased] + - Update Cardano Serialization Lib to 12.0.0-alpha.19 [Issue 521](https://github.com/IntersectMBO/govtool/issues/521) - Add generate jsonld function [Issue 451](https://github.com/IntersectMBO/govtool/issues/451) - Create GA review subbmision page [Issue 362](https://github.com/IntersectMBO/govtool/issues/362) @@ -18,9 +19,13 @@ changes. - Add on-chain inputs validation [Issue 377](https://github.com/IntersectMBO/govtool/issues/377) - Add hash and validation of the metadata [Issue 378](https://github.com/IntersectMBO/govtool/issues/378) - Add githubusercontent.com and ipfs.io to content security policy header [Issue 451](https://github.com/IntersectMBO/govtool/issues/451) -- Add frontend test workflow on github actions [Issue 500](https://github.com/IntesectMBO/govtool/issues/500) -- Add type check & lint to github actions [Issue 512](https://github.com/IntesectMBO/govtool/issues/512) -- Add eslint & prettier to frontend package [Issue 166](https://github.com/IntesectMBO/govtool/issues/166) +- Add frontend test workflow on github actions [Issue 500](https://github.com/IntersectMBO/govtool/issues/500) +- Add type check & lint to github actions [Issue 512](https://github.com/IntersectMBO/govtool/issues/512) +- Add eslint & prettier to frontend package [Issue 166](https://github.com/IntersectMBO/govtool/issues/166) +- Extend the eslint config to apply to the style guide of the project [Issue 514](https://github.com/IntersectMBO/govtool/issues/514) +- Fix all the existing eslint errors [Issue 514](https://github.com/IntersectMBO/govtool/issues/514) +- Fix all the existing typescript errors [Issue 514](https://github.com/IntersectMBO/govtool/issues/514) +- Fix endless spinner on a dashboard [Issue 539](https://github.com/IntersectMBO/govtool/issues/539) ### Added @@ -32,6 +37,8 @@ changes. - Vitest unit tests added for utils functions [Issue 81](https://github.com/IntersectMBO/govtool/issues/81) - i18next library added to FE [Issue 80](https://github.com/IntersectMBO/govtool/issues/80) - Add possibility to vote on behalf of myself - Sole Voter [Issue 119](https://github.com/IntersectMBO/govtool/issues/119) +- Added Nix Flakes configurtion to handle unified developers setup [Issue 526](https://github.com/IntersectMBO/govtool/issues/526) +- Add missing test to utils [Issue 500](https://github.com/IntersectMBO/govtool/issues/500). ### Fixed @@ -48,6 +55,7 @@ changes. - Fixed deployment scripts to address [Issue 171](https://github.com/IntersectMBO/govtool/issues/171). - Fixed get drep voting power incorrectly executed endpoint [Issue 280](https://github.com/IntersectMBO/govtool/issues/280). - Fixed CSP settings to allow error reports with Sentry [Issue 291](https://github.com/IntersectMBO/govtool/issues/291). +- Fix frontend package tests [Issue 500](https://github.com/IntersectMBO/govtool/issues/500). ### Changed diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e2ceb99de..9fdd28480 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -167,6 +167,7 @@ Try to keep branches up-to-date with main (not strict requirement though). Once merged to main, please delete the branch. **Tip:** Use Github's merge button in PRs to merge with commit. +This strategy helps us operate on the commits you've delivered: it's easier to [cherry-pick a merge commit](https://git-scm.com/docs/git-cherry-pick#Documentation/git-cherry-pick.txt--mltparent-numbergt) than a series of commits, and it's also easier to [revert changes using a merge commit](https://git-scm.com/docs/git-revert#Documentation/git-revert.txt--mparent-number) instead of a series of reverts. If a branch is outdated, use the rebase button in PRs to rebase feature branches (NOT update via merge). #### Rationale @@ -229,19 +230,21 @@ TODO - Choose ticket/issue to work on from the project, move ticket from `todo` to `in progress`. - Create [well named](#branch-naming) branch from `develop` add changes, then make a pull request back to the `develop` branch. - If the changes are not ready for review then feel free to create a draft PR, and link this to the ticket/issue. +- When the PR is ready for review move the ticket from `in progress` to `in review`. Remember to change the state of the PR from draft to actual PR. - Developers should review each other's pull requests, and should be requested via [CODEOWNERS](./CODEOWNERS). - Unit tests are run on each pull request to `develop`. -- Once tests pass and peer review is done the branch can be merged into `develop` by author and then deployed to the dev environment (manually for now). -- The ticket status can then be moved ticket to `in QA` making sure that the PR/branch has been added to the ticket/issue as a comment. +- After a review remember to address all the requests of changes since they are blocking PR from being merged. +- Once tests pass and peer review is done the branch can be merged into `develop` by author and then deployed to the dev environment (manually). +- The ticket status can then be moved to `in QA` making sure that the PR/branch has been added to the ticket/issue as a comment. ### QA Workflow - Choose ticket from `in QA`. - Merge in the ticket's changes from `develop` branch into `test` branch. -- Deploy to test environment (manually for now). +- Deploy to test environment is performed automatically once the ticket is merged to the `test` branch. - The QA tests the deployed test environment against the ticket. - If QA agrees that the code is good, they can make a PR from `test` branch to `staging` branch where end-to-end and performance tests are run. -- If tests pass, then QA or tech lead can merge and deploy to staging environment (manually for now). +- If tests pass, then QA or tech lead can merge and deploy to staging environment (automatically). - Moving ticket to `staging` status this ready for PO check. ### PO Workflow diff --git a/flake.lock b/flake.lock new file mode 100644 index 000000000..176d430c2 --- /dev/null +++ b/flake.lock @@ -0,0 +1,44 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1679410443, + "narHash": "sha256-xDHO/jixWD+y5pmW5+2q4Z4O/I/nA4MAa30svnZKK+M=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "c9ece0059f42e0ab53ac870104ca4049df41b133", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "rev": "c9ece0059f42e0ab53ac870104ca4049df41b133", + "type": "github" + } + }, + "node_nixpkgs": { + "locked": { + "lastModified": 1696748673, + "narHash": "sha256-UbPHrH4dKN/66EpfFpoG4St4XZYDX9YcMVRQGWzAUNA=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "9957cd48326fe8dbd52fdc50dd2502307f188b0d", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "rev": "9957cd48326fe8dbd52fdc50dd2502307f188b0d", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs", + "node_nixpkgs": "node_nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 000000000..922d4d454 --- /dev/null +++ b/flake.nix @@ -0,0 +1,27 @@ +{ + description = "GovTool and utilities monorepo."; + + inputs.nixpkgs.url = + "github:nixos/nixpkgs/c9ece0059f42e0ab53ac870104ca4049df41b133"; + inputs.node_nixpkgs.url = + "github:nixos/nixpkgs/9957cd48326fe8dbd52fdc50dd2502307f188b0d"; + + outputs = { self, nixpkgs, node_nixpkgs }: { + packages = let + supportedSystems = + [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; + in builtins.listToAttrs (map (system: + let + pkgs = import nixpkgs { inherit system; config.allowBroken = true; }; + npkgs = import node_nixpkgs { inherit system; }; + in { + name = system; + value = { + scripts = pkgs.callPackage ./scripts/govtool { inherit pkgs; }; + infra = pkgs.callPackage ./infra/terraform { inherit pkgs; }; + backend = pkgs.callPackage ./govtool/backend { inherit pkgs; }; + frontend = pkgs.callPackage ./govtool/frontend { pkgs = npkgs; }; + }; + }) supportedSystems); + }; +} diff --git a/govtool/backend/.envrc b/govtool/backend/.envrc new file mode 100644 index 000000000..4b15e214d --- /dev/null +++ b/govtool/backend/.envrc @@ -0,0 +1,3 @@ +source_up_if_exists +watch_file vva-be.cabal +use flake --extra-experimental-features nix-command --extra-experimental-features flakes ../..#backend diff --git a/govtool/backend/README.md b/govtool/backend/README.md index 4f6211a41..bf9225f57 100644 --- a/govtool/backend/README.md +++ b/govtool/backend/README.md @@ -18,29 +18,24 @@ You will need your `cardano-node` and `cardano-db-sync` to be compatible with Sa [`sancho` testnet config files](https://sancho.network/tutorials/start-node/) -### Using Nix +### Using Nix and Direnv 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). -2. Enter `govtool/backend` directory: +2. Get [direnv](https://direnv.net/). - ```sh - cd govtool/backend - ``` - -3. Allow broken nix packages +3. Enter `govtool/backend` directory: ```sh - export NIXPKGS_ALLOW_BROKEN=1 + cd govtool/backend ``` - This is due to `openapi3` package being marked as broken. -4. Run `nix-shell` +4. Allow direnv to setup your environment: ```sh - nix-shell + direnv allow ``` 5. Update cabal & build project diff --git a/govtool/backend/default.nix b/govtool/backend/default.nix index aab2212bc..cb9eb4aae 100644 --- a/govtool/backend/default.nix +++ b/govtool/backend/default.nix @@ -1,10 +1,10 @@ -{ pkgs ? import {} }: +{ pkgs ? import { } }: let # This is the version of the Haskell compiler we reccommend using. ghcPackages = pkgs.haskell.packages.ghc927; - additionalTools = drv: pkgs.haskell.lib.addBuildTools drv (with ghcPackages; - [ + additionalTools = drv: + pkgs.haskell.lib.addBuildTools drv (with ghcPackages; [ cabal-install haskell-language-server lzma @@ -16,7 +16,17 @@ let project = ghcPackages.developPackage { root = ./.; modifier = additionalTools; - overrides = self: super: { openapi3 = pkgs.haskell.lib.dontCheck super.openapi3; }; + overrides = self: super: { + openapi3 = pkgs.haskell.lib.dontCheck super.openapi3; + }; }; -in -project +in project.overrideAttrs (oldAttrs: { + shellHook = '' + function warn() { tput setaf $2; echo "$1"; tput sgr0; } + + tput bold + warn "Welcome to GovTool!" 4 + warn "This is a backend development shell." 4 + warn "Read the govtool/backend/README.md to get more info about this module." 8 + '' + (oldAttrs.shellHook or ""); +}) diff --git a/govtool/backend/shell.nix b/govtool/backend/shell.nix deleted file mode 100644 index e020a95a2..000000000 --- a/govtool/backend/shell.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ pkgs ? import {} }: -let - project = import ./default.nix { inherit pkgs; }; -in -project.overrideAttrs (attrs: { - buildInputs = attrs.buildInputs ++ (with pkgs; [ - awscli - docker - git - gnumake - ]); - - shellHook = '' - ln -s ${project}/libexec/yarn-nix-example/node_modules node_modules - ''; -}) diff --git a/govtool/backend/sources.nix b/govtool/backend/sources.nix deleted file mode 100644 index c8b57f261..000000000 --- a/govtool/backend/sources.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ - pkgs = import - (builtins.fetchGit { - url = "https://github.com/NixOS/nixpkgs.git"; - rev = "c9ece0059f42e0ab53ac870104ca4049df41b133"; - }) - { }; -} diff --git a/govtool/frontend/.env.example b/govtool/frontend/.env.example deleted file mode 100644 index cacf4a05a..000000000 --- a/govtool/frontend/.env.example +++ /dev/null @@ -1,5 +0,0 @@ -VITE_BASE_URL= -VITE_NETWORK_FLAG= -VITE_SENTRY_DSN= -VITE_GTM_ID= -VITE_IS_DEV= \ No newline at end of file diff --git a/govtool/frontend/.envrc b/govtool/frontend/.envrc new file mode 100644 index 000000000..887f4dc16 --- /dev/null +++ b/govtool/frontend/.envrc @@ -0,0 +1,17 @@ +# vim: set ft=bash + +source_up +watch_file default.nix yarn.lock +env_vars_required CARDANO_NETWORK GTM_ID SENTRY_DSN + +if [[ "${CARDANO_NETWORK}" = "mainnet" ]]; then + export VITE_NETWORK_FLAG=1 +else + export VITE_NETWORK_FLAG=0 +fi +export VITE_BASE_URL=http://localhost +export VITE_IS_DEV=true +export VITE_GTM_ID="${GTM_ID}" +export VITE_SENTRY_DSN="${SENTRY_DSN}" + +use flake --extra-experimental-features nix-command --extra-experimental-features flakes ../..#frontend diff --git a/govtool/frontend/.eslintrc.cjs b/govtool/frontend/.eslintrc.cjs index b6cee55b8..89a5aff94 100644 --- a/govtool/frontend/.eslintrc.cjs +++ b/govtool/frontend/.eslintrc.cjs @@ -4,73 +4,105 @@ module.exports = { es2021: true, }, extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:storybook/recommended', - 'plugin:react/recommended', - 'plugin:jest/recommended', - 'airbnb', + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:storybook/recommended", + "plugin:react/recommended", + "plugin:react-hooks/recommended", + "plugin:jest/recommended", + "airbnb", ], - plugins: ['@typescript-eslint', 'react', 'jest'], - parser: '@typescript-eslint/parser', + plugins: ["@typescript-eslint", "react", "jest"], + parser: "@typescript-eslint/parser", parserOptions: { tsconfigRootDir: __dirname, ecmaFeatures: { jsx: true, }, ecmaVersion: 2018, - sourceType: 'module', - project: './tsconfig.json', - files: ['*.ts', '*.tsx'], + sourceType: "module", + project: "./tsconfig.json", + files: ["*.ts", "*.tsx"], }, rules: { - quotes: 'off', - semi: ['error', 'always'], - 'linebreak-style': ['error', 'unix'], - 'no-redeclare': 'off', - 'no-undef': 'off', - 'no-prototype-builtins': 'off', - 'no-unused-vars': 'off', - 'comma-dangle': 'off', - 'operator-linebreak': 'off', - 'implicit-arrow-linebreak': 'off', - 'consistent-return': 'off', + "array-callback-return": "off", + curly: "off", + indent: "off", + quotes: "off", + semi: ["error", "always"], + "linebreak-style": ["error", "unix"], + "no-redeclare": "off", + "no-undef": "off", + "no-prototype-builtins": "off", + "nonblock-statement-body-position": "off", - '@typescript-eslint/no-redeclare': ['error'], - '@typescript-eslint/no-unused-vars': ['error'], + // TODO: This rule should be enabled in the future + "no-param-reassign": "off", - 'import/prefer-default-export': 'off', - 'import/extensions': 'off', - 'import/no-unresolved': 'off', - 'import/no-extraneous-dependencies': [ - 'error', + // TODO: This rule should be enabled in the future + "no-plusplus": "off", + + // TODO: This rule should be enabled in the future + "no-nested-ternary": "off", + + "no-unused-vars": "off", + "no-use-before-define": "off", + "comma-dangle": "off", + "operator-linebreak": "off", + "implicit-arrow-linebreak": "off", + "consistent-return": "off", + "no-shadow": "off", + "function-paren-newline": "off", + "object-curly-newline": "off", + + "@typescript-eslint/no-shadow": ["error"], + "@typescript-eslint/no-redeclare": ["error"], + "@typescript-eslint/no-unused-vars": ["error"], + + "import/prefer-default-export": "off", + "import/extensions": "off", + "import/no-unresolved": "off", + "import/no-extraneous-dependencies": [ + "error", { - devDependencies: ['**/*.stories.{ts,tsx}', '**/*.test.{ts,tsx}'], + devDependencies: ["**/*.stories.{ts,tsx}", "**/*.test.{ts,tsx}"], }, ], - 'react/jsx-no-bind': ['warn'], - 'react/jsx-uses-react': ['error'], - 'react/react-in-jsx-scope': 'off', - 'react/jsx-filename-extension': ['error', { extensions: ['.tsx'] }], - 'react/jsx-props-no-spreading': 'off', - 'react/jsx-fragments': 'off', - 'react/display-name': 'off', - 'react/prop-types': 'off', - 'react/function-component-definition': [ - 'error', + "jsx-a11y/anchor-is-valid": "off", + "jsx-a11y/no-noninteractive-element-interactions": "off", + "jsx-a11y/click-events-have-key-events": "off", + + "react/display-name": "off", + "react/jsx-curly-newline": "off", + "react/jsx-filename-extension": ["error", { extensions: [".tsx"] }], + "react/jsx-fragments": "off", + "react/jsx-no-bind": "warn", + "react/jsx-no-useless-fragment": "off", + "react/jsx-wrap-multilines": "off", + "react/jsx-props-no-spreading": "off", + "react/jsx-uses-react": "error", + "react/jsx-one-expression-per-line": "off", + "react/prop-types": "off", + "react/react-in-jsx-scope": "off", + "react/function-component-definition": [ + "error", { - namedComponents: 'arrow-function', - unnamedComponents: 'arrow-function', + namedComponents: "arrow-function", + unnamedComponents: "arrow-function", }, ], - 'react/require-default-props': 'off', + "react/require-default-props": "off", + + // TODO: This rule should be enabled in the future + "react-hooks/exhaustive-deps": "off", }, ignorePatterns: [ - '.eslintrc.cjs', - '.storybook/', - '.vite/', - 'dist/', - 'node_modules/', + ".eslintrc.cjs", + ".storybook/", + ".vite/", + "dist/", + "node_modules/", + "vite.config.ts", ], }; diff --git a/govtool/frontend/.prettierrc b/govtool/frontend/.prettierrc index f77586b27..f52db5fca 100644 --- a/govtool/frontend/.prettierrc +++ b/govtool/frontend/.prettierrc @@ -1,6 +1,6 @@ { "semi": true, - "singleQuote": true, + "singleQuote": false, "trailingComma": "all", "printWidth": 80 } diff --git a/govtool/frontend/Dockerfile b/govtool/frontend/Dockerfile index e0755d481..4e06933bf 100644 --- a/govtool/frontend/Dockerfile +++ b/govtool/frontend/Dockerfile @@ -1,5 +1,5 @@ FROM node:18-alpine as builder -ARG VITE_BASE_URL=vv-be.localhost +ARG VITE_BASE_URL ARG VITE_GTM_ID ARG VITE_NETWORK_FLAG=0 ARG VITE_SENTRY_DSN diff --git a/govtool/frontend/README.md b/govtool/frontend/README.md index d8ace9d3b..ec8be6bdb 100644 --- a/govtool/frontend/README.md +++ b/govtool/frontend/README.md @@ -2,13 +2,13 @@ Installed on your machine: -1. Node.js >= 16 ([official website](https://nodejs.org/en)) +1. Node.js >= 18 ([official website](https://nodejs.org/en)) 2. npm or yarn - for package management Clone the project ```bash - git clone https://github.com/IntersectMBO/govtool +git clone https://github.com/IntersectMBO/govtool ``` Fill .env based on env.example file @@ -16,19 +16,19 @@ Fill .env based on env.example file Go to the project directory ```bash - cd voltaire-era/govtool/frontend +cd voltaire-era/govtool/frontend ``` Install dependencies ```bash - npm install +npm install ``` or ```bash - yarn install +yarn install ``` Start the server @@ -43,47 +43,6 @@ or yarn dev ``` -#### Using nix - -1. Get [Nix](https://nixos.org/download). - -2. Enter `govtool/frontend` directory: - -```sh -cd govtool/frontend -``` - -3. Run `nix-shell` - -```sh -nix-shell -``` - -4. Run project - -```sh -npm run dev -``` - -##### Alternative - -You can skip point 3 by using an automatic direnv configuration: - -Install [`direnv`](https://direnv.net/): - -```sh -nix-env -i direnv -``` - -Allow direnv configuration in `govtool/frontend`: - -```sh -direnv allow govtool/frontend -``` - -From now on, once you enter the `govtool/frontend` the `nix-shell` with proper -configuration will be propagated. - ## Developing ### Frontend Built With @@ -107,7 +66,6 @@ configuration will be propagated. Install [`Git`](https://git-scm.com/) - version control Recommendetd [`React developer tools`](https://react.dev/learn/react-developer-tools) - ## To Develop ### Standard way @@ -124,42 +82,32 @@ npm install npm run dev ``` -### Using nix +#### Using Nix and Direnv 1. Get [Nix](https://nixos.org/download). -2. Enter `govtool/frontend` directory: -```sh -cd govtool/frontend -``` +2. Get [direnv](https://direnv.net/). -3. Run `nix-shell` -```sh -nix-shell -``` +3. Fill .envrc based on envrc.example file in project root. + +4. Enter `govtool/frontend` directory: -4. Run project ```sh -npm run dev +cd govtool/frontend ``` -##### Alternative - -You can skip point 3 by using an automatic direnv configuration: +5. Allow direnv to setup your environment: -Install [`direnv`](https://direnv.net/): ```sh -nix-env -i direnv +direnv allow ``` -Allow direnv configuration in `govtool/frontend`: +5. Run project + ```sh -direnv allow govtool/frontend +yarn dev ``` -From now on, once you enter the `govtool/frontend` the `nix-shell` with proper -configuration will be propagated. - ### Users The GovTool application can read and display data from the Cardano chain using REST API. diff --git a/govtool/frontend/default.nix b/govtool/frontend/default.nix index e96a00e56..979ab1c95 100644 --- a/govtool/frontend/default.nix +++ b/govtool/frontend/default.nix @@ -5,6 +5,18 @@ let src = ./.; packageJSON = ./package.json; yarnLock = ./yarn.lock; + nodejs = pkgs.nodejs_18; }; in -project +project.overrideAttrs (attrs: { + shellHook = '' + function warn() { tput setaf $2; echo "$1"; tput sgr0; } + + tput bold + warn "Welcome to GovTool!" 4 + warn "This is a frontend development shell." 4 + warn "Read the govtool/frontend/README.md to get more info about this module." 8 + rm -rf ./node_modules + ln -s ${project.out}/libexec/voltaire-voting-app/node_modules ./node_modules + ''; +}) diff --git a/govtool/frontend/package-lock.json b/govtool/frontend/package-lock.json index 1327e6976..45ed83f88 100644 --- a/govtool/frontend/package-lock.json +++ b/govtool/frontend/package-lock.json @@ -29,7 +29,6 @@ "jsonld": "^8.3.2", "keen-slider": "^6.8.5", "patch-package": "^8.0.0", - "postinstall-postinstall": "^2.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-gtm-module": "^2.0.11", @@ -16960,12 +16959,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/postinstall-postinstall": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz", - "integrity": "sha512-7hQX6ZlZXIoRiWNrbMQaLzUUfH+sSx39u8EJ9HYuDc1kLo9IXKWjM5RSquZN1ad5GnH8CGFM78fsAAQi3OKEEQ==", - "hasInstallScript": true - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", diff --git a/govtool/frontend/package.json b/govtool/frontend/package.json index 907d64dc1..d97403562 100644 --- a/govtool/frontend/package.json +++ b/govtool/frontend/package.json @@ -4,18 +4,19 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite", "build": "vite build", + "build-storybook": "storybook build", + "chromatic": "chromatic", + "dev": "vite", + "format": "prettier --write src", "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "tsc": "npx tsc --noEmit --skipLibCheck", + "postinstall": "patch-package", "preview": "vite preview", "storybook": "storybook dev -p 6006", - "build-storybook": "storybook build", - "test-storybook": "test-storybook", - "chromatic": "chromatic", "test": "vitest", + "test-storybook": "test-storybook", "test:watch": "vitest watch", - "postinstall": "patch-package" + "tsc": "npx tsc --noEmit --skipLibCheck" }, "dependencies": { "@emotion/react": "^11.11.1", @@ -38,7 +39,6 @@ "jsonld": "^8.3.2", "keen-slider": "^6.8.5", "patch-package": "^8.0.0", - "postinstall-postinstall": "^2.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-gtm-module": "^2.0.11", @@ -91,9 +91,6 @@ "optionalDependencies": { "@rollup/rollup-linux-arm64-musl": "4.12.0" }, - "resolutions": { - "jackspeak": "2.1.1" - }, "readme": "ERROR: No README data found!", "_id": "voltaire-voting-app@0.0.0" } diff --git a/govtool/frontend/public/icons/Download.svg b/govtool/frontend/public/icons/Download.svg index c39977182..d283aaaef 100644 --- a/govtool/frontend/public/icons/Download.svg +++ b/govtool/frontend/public/icons/Download.svg @@ -1,5 +1,5 @@ - - - + + + diff --git a/govtool/frontend/src/App.tsx b/govtool/frontend/src/App.tsx index f05f2396f..7db190366 100644 --- a/govtool/frontend/src/App.tsx +++ b/govtool/frontend/src/App.tsx @@ -30,14 +30,13 @@ import { removeItemFromLocalStorage, } from "@utils"; import { SetupInterceptors } from "./services"; -import { useGetVoterInfo, useWalletConnectionListener } from "./hooks"; +import { useWalletConnectionListener } from "./hooks"; import { RegisterAsSoleVoter } from "./pages/RegisterAsSoleVoter"; import { CreateGovernanceAction } from "./pages/CreateGovernanceAction"; -export default function App() { - const { enable, setVoter, setIsDrepLoading } = useCardano(); +export default () => { + const { enable } = useCardano(); const navigate = useNavigate(); - const { data } = useGetVoterInfo(); const { modal, openModal, modals } = useModal(); useWalletConnectionListener(); @@ -46,18 +45,11 @@ export default function App() { SetupInterceptors(navigate); }, []); - useEffect(() => { - setIsDrepLoading(true); - setVoter(data); - const timer = setTimeout(() => setIsDrepLoading(false), 1000); - - return () => clearTimeout(timer); - }, [data?.isRegisteredAsDRep, data?.isRegisteredAsSoleVoter]); - const checkTheWalletIsActive = useCallback(() => { - const hrefCondition = window.location.pathname === PATHS.home - || window.location.pathname === PATHS.governanceActions - || window.location.pathname === PATHS.governanceActionsAction; + const hrefCondition = + window.location.pathname === PATHS.home || + window.location.pathname === PATHS.governanceActions || + window.location.pathname === PATHS.governanceActionsAction; const walletName = getItemFromLocalStorage(`${WALLET_LS_KEY}_name`); if (window.cardano) { @@ -68,8 +60,8 @@ export default function App() { } } if ( - (!window.cardano && walletName) - || (walletName && !Object.keys(window.cardano).includes(walletName)) + (!window.cardano && walletName) || + (walletName && !Object.keys(window.cardano).includes(walletName)) ) { if (!hrefCondition) { navigate(PATHS.home); @@ -88,10 +80,7 @@ export default function App() { } /> - } - /> + } /> } @@ -136,13 +125,15 @@ export default function App() { open={Boolean(modals[modal.type].component)} handleClose={ !modals[modal.type].preventDismiss - ? callAll(modals[modal.type]?.onClose, () => openModal({ type: "none", state: null })) + ? callAll(modals[modal.type]?.onClose, () => + openModal({ type: "none", state: null }), + ) : undefined } > - {modals[modal.type]?.component ?? <>} + {modals[modal.type].component!} )} ); -} +}; diff --git a/govtool/frontend/src/components/atoms/Background.tsx b/govtool/frontend/src/components/atoms/Background.tsx index 6470f53aa..62ca862a1 100644 --- a/govtool/frontend/src/components/atoms/Background.tsx +++ b/govtool/frontend/src/components/atoms/Background.tsx @@ -1,7 +1,7 @@ -import { ReactNode } from 'react'; +import { ReactNode } from "react"; -import { IMAGES } from '@consts'; -import { useScreenDimension } from '@/hooks'; +import { IMAGES } from "@consts"; +import { useScreenDimension } from "@/hooks"; export const Background = ({ children, @@ -17,29 +17,31 @@ export const Background = ({ return ( <> bg-orange {children} bg-blue ); diff --git a/govtool/frontend/src/components/atoms/Button.tsx b/govtool/frontend/src/components/atoms/Button.tsx index 106dfde45..97b9f100e 100644 --- a/govtool/frontend/src/components/atoms/Button.tsx +++ b/govtool/frontend/src/components/atoms/Button.tsx @@ -1,9 +1,9 @@ -import { Button as MUIButton } from '@mui/material'; -import { ButtonProps } from '.'; +import { Button as MUIButton } from "@mui/material"; +import { ButtonProps } from "."; export const Button = ({ - size = 'large', - variant = 'contained', + size = "large", + variant = "contained", sx, ...props }: ButtonProps) => { @@ -17,7 +17,7 @@ export const Button = ({ return ( } sx={{ - '& .MuiSvgIcon-root': { fontSize: 18 }, - color: errorMessage ? 'red' : '#0033AD', + "& .MuiSvgIcon-root": { fontSize: 18 }, + color: errorMessage ? "red" : "#0033AD", ...sx, }} {...props} diff --git a/govtool/frontend/src/components/atoms/ClickOutside.tsx b/govtool/frontend/src/components/atoms/ClickOutside.tsx index 0863c76ab..86266bf2a 100644 --- a/govtool/frontend/src/components/atoms/ClickOutside.tsx +++ b/govtool/frontend/src/components/atoms/ClickOutside.tsx @@ -1,16 +1,16 @@ -import React, { useRef, useEffect } from 'react'; +import React, { useRef, useEffect, RefObject } from "react"; -const useOutsideClick = (ref: any, onClick: () => void) => { +const useOutsideClick = (ref: RefObject, onClick: () => void) => { useEffect(() => { - document.addEventListener('mousedown', (e) => { - if (ref.current && !ref.current.contains(e.target)) { + document.addEventListener("mousedown", (e) => { + if (ref.current && !ref.current.contains(e.target as Node)) { onClick(); } }); return () => { - document.removeEventListener('mousedown', (e) => { - if (ref.current && !ref.current.contains(e.target)) { + document.removeEventListener("mousedown", (e) => { + if (ref.current && !ref.current.contains(e.target as Node)) { onClick(); } }); diff --git a/govtool/frontend/src/components/atoms/CopyButton.tsx b/govtool/frontend/src/components/atoms/CopyButton.tsx index 9787651ad..65601c718 100644 --- a/govtool/frontend/src/components/atoms/CopyButton.tsx +++ b/govtool/frontend/src/components/atoms/CopyButton.tsx @@ -1,8 +1,8 @@ -import { useMemo } from 'react'; +import { useMemo } from "react"; -import { ICONS } from '@consts'; -import { useSnackbar } from '@context'; -import { useTranslation } from '@hooks'; +import { ICONS } from "@consts"; +import { useSnackbar } from "@context"; +import { useTranslation } from "@hooks"; interface Props { isChecked?: boolean; @@ -15,7 +15,7 @@ export const CopyButton = ({ isChecked, text, variant }: Props) => { const { t } = useTranslation(); const iconSrc = useMemo(() => { - if (variant === 'blue') { + if (variant === "blue") { return ICONS.copyBlueIcon; } @@ -32,11 +32,11 @@ export const CopyButton = ({ isChecked, text, variant }: Props) => { alt="copy" onClick={(e) => { navigator.clipboard.writeText(text); - addSuccessAlert(t('alerts.copiedToClipboard')); + addSuccessAlert(t("alerts.copiedToClipboard")); e.stopPropagation(); }} src={iconSrc} - style={{ cursor: 'pointer' }} + style={{ cursor: "pointer" }} /> ); }; diff --git a/govtool/frontend/src/components/atoms/DrawerLink.tsx b/govtool/frontend/src/components/atoms/DrawerLink.tsx index f62d343b9..cb9a77d27 100644 --- a/govtool/frontend/src/components/atoms/DrawerLink.tsx +++ b/govtool/frontend/src/components/atoms/DrawerLink.tsx @@ -14,9 +14,7 @@ type LinkProps = { }; export const DrawerLink: FC = ({ ...props }) => { - const { - dataTestId, label, navTo, icon, activeIcon, onClick, - } = props; + const { dataTestId, label, navTo, icon, activeIcon, onClick } = props; const { palette: { highlightBlue }, } = theme; @@ -35,14 +33,15 @@ export const DrawerLink: FC = ({ ...props }) => { display: "block", borderRadius: 100, })} - children={({ isActive }) => ( + > + {({ isActive }) => ( {activeIcon && icon && ( - icon + icon )} = ({ ...props }) => { )} - /> + ); }; diff --git a/govtool/frontend/src/components/atoms/FormErrorMessage.tsx b/govtool/frontend/src/components/atoms/FormErrorMessage.tsx index 4dafb30d8..2bd3a37cf 100644 --- a/govtool/frontend/src/components/atoms/FormErrorMessage.tsx +++ b/govtool/frontend/src/components/atoms/FormErrorMessage.tsx @@ -1,21 +1,20 @@ -import { Typography } from '@mui/material'; +import { Typography } from "@mui/material"; -import { FormErrorMessageProps } from './types'; +import { FormErrorMessageProps } from "./types"; export const FormErrorMessage = ({ errorMessage, errorStyles, -}: FormErrorMessageProps) => ( +}: FormErrorMessageProps) => errorMessage && ( - - {errorMessage} - - ) -); + + {errorMessage} + + ); diff --git a/govtool/frontend/src/components/atoms/FormHelpfulText.tsx b/govtool/frontend/src/components/atoms/FormHelpfulText.tsx index 437c69330..fc969d48c 100644 --- a/govtool/frontend/src/components/atoms/FormHelpfulText.tsx +++ b/govtool/frontend/src/components/atoms/FormHelpfulText.tsx @@ -1,21 +1,20 @@ -import { Typography } from '@mui/material'; +import { Typography } from "@mui/material"; -import { FormHelpfulTextProps } from './types'; +import { FormHelpfulTextProps } from "./types"; export const FormHelpfulText = ({ helpfulText, helpfulTextStyle, -}: FormHelpfulTextProps) => ( +}: FormHelpfulTextProps) => helpfulText && ( - - {helpfulText} - - ) -); + + {helpfulText} + + ); diff --git a/govtool/frontend/src/components/atoms/HighlightedText.tsx b/govtool/frontend/src/components/atoms/HighlightedText.tsx index 0115f7763..9b18e8dc0 100644 --- a/govtool/frontend/src/components/atoms/HighlightedText.tsx +++ b/govtool/frontend/src/components/atoms/HighlightedText.tsx @@ -1,4 +1,4 @@ -import { Typography } from '@mui/material'; +import { Typography } from "@mui/material"; interface Props { text: string; @@ -10,19 +10,20 @@ interface Props { export const HighlightedText = ({ text, - searchPhrase = '', + searchPhrase = "", fontSize = 12, fontWeight = 400, - color = 'inherit', + color = "inherit", }: Props) => { - const regex = new RegExp(`(${searchPhrase})`, 'gi'); + const regex = new RegExp(`(${searchPhrase})`, "gi"); const parts = text.split(regex); return ( <> {parts.map((part, index) => ( diff --git a/govtool/frontend/src/components/atoms/InfoText.tsx b/govtool/frontend/src/components/atoms/InfoText.tsx index c553395f4..768ac2b0b 100644 --- a/govtool/frontend/src/components/atoms/InfoText.tsx +++ b/govtool/frontend/src/components/atoms/InfoText.tsx @@ -1,4 +1,4 @@ -import { InfoTextProps, Typography } from '.'; +import { InfoTextProps, Typography } from "."; export const InfoText = ({ label, sx }: InfoTextProps) => ( diff --git a/govtool/frontend/src/components/atoms/Input.tsx b/govtool/frontend/src/components/atoms/Input.tsx index 77ef1b259..1901f1e7f 100644 --- a/govtool/frontend/src/components/atoms/Input.tsx +++ b/govtool/frontend/src/components/atoms/Input.tsx @@ -10,9 +10,7 @@ import { InputBase } from "@mui/material"; import { InputProps } from "./types"; export const Input = forwardRef( - ({ - errorMessage, dataTestId, onBlur, onFocus, sx, ...rest - }, ref) => { + ({ errorMessage, dataTestId, onBlur, onFocus, sx, ...rest }, ref) => { const id = useId(); const inputRef = useRef(null); @@ -28,11 +26,12 @@ export const Input = forwardRef( useImperativeHandle( ref, - () => ({ - focus: handleFocus, - blur: handleBlur, - ...inputRef.current, - } as unknown as HTMLInputElement), + () => + ({ + focus: handleFocus, + blur: handleBlur, + ...inputRef.current, + } as unknown as HTMLInputElement), [handleBlur, handleFocus], ); diff --git a/govtool/frontend/src/components/atoms/Link.tsx b/govtool/frontend/src/components/atoms/Link.tsx index 1f397cf67..78fc8bccd 100644 --- a/govtool/frontend/src/components/atoms/Link.tsx +++ b/govtool/frontend/src/components/atoms/Link.tsx @@ -39,7 +39,8 @@ export const Link: FC = ({ ...props }) => { if (!isConnectWallet) disconnectWallet(); if (onClick) onClick(); }} - children={({ isActive }) => ( + > + {({ isActive }) => ( = ({ ...props }) => { {label} )} - /> + ); }; diff --git a/govtool/frontend/src/components/atoms/LoadingButton.tsx b/govtool/frontend/src/components/atoms/LoadingButton.tsx index fbeb8389c..11a22d56f 100644 --- a/govtool/frontend/src/components/atoms/LoadingButton.tsx +++ b/govtool/frontend/src/components/atoms/LoadingButton.tsx @@ -1,12 +1,12 @@ -import { Button, CircularProgress } from '@mui/material'; +import { Button, CircularProgress } from "@mui/material"; -import { LoadingButtonProps } from './types'; +import { LoadingButtonProps } from "./types"; export const LoadingButton = ({ isLoading, disabled, children, - size = 'large', + size = "large", sx, ...rest }: LoadingButtonProps) => { @@ -24,7 +24,7 @@ export const LoadingButton = ({ {...rest} > {isLoading && ( - + )} {children} diff --git a/govtool/frontend/src/components/atoms/Radio.tsx b/govtool/frontend/src/components/atoms/Radio.tsx index 0bd64e07a..538fa7deb 100644 --- a/govtool/frontend/src/components/atoms/Radio.tsx +++ b/govtool/frontend/src/components/atoms/Radio.tsx @@ -1,5 +1,6 @@ -import { Box, Typography } from '@mui/material'; -import { UseFormRegister, UseFormSetValue } from 'react-hook-form'; +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { Box, Typography } from "@mui/material"; +import { UseFormRegister, UseFormSetValue } from "react-hook-form"; type RadioProps = { isChecked: boolean; @@ -12,9 +13,7 @@ type RadioProps = { }; export const Radio = ({ ...props }: RadioProps) => { - const { - isChecked, name, setValue, title, value, dataTestId, register - } = + const { isChecked, name, setValue, title, value, dataTestId, register } = props; const handleClick = () => { @@ -28,27 +27,27 @@ export const Radio = ({ ...props }: RadioProps) => { borderRadius={2} p={0.2} border={isChecked ? 1 : 0} - borderColor={isChecked ? 'specialCyanBorder' : 'white'} - sx={[{ '&:hover': { color: 'blue', cursor: 'pointer' } }]} + borderColor={isChecked ? "specialCyanBorder" : "white"} + sx={[{ "&:hover": { color: "blue", cursor: "pointer" } }]} flex={1} > {title} diff --git a/govtool/frontend/src/components/atoms/ScrollToManage.tsx b/govtool/frontend/src/components/atoms/ScrollToManage.tsx index 94eadd5f0..543ca62c2 100644 --- a/govtool/frontend/src/components/atoms/ScrollToManage.tsx +++ b/govtool/frontend/src/components/atoms/ScrollToManage.tsx @@ -1,13 +1,14 @@ -import { useEffect } from 'react'; -import { useLocation } from 'react-router-dom'; -import { PATHS } from '@/consts'; +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { useEffect } from "react"; +import { useLocation } from "react-router-dom"; +import { PATHS } from "@/consts"; export function debounce( fn: (...params: any) => void, wait: number, ): (...params: any) => void { let timer: any = null; - return function (...params: any) { + return (...params: any) => { clearTimeout(timer); timer = setTimeout(() => { fn(...params); @@ -44,8 +45,8 @@ export const ScrollToManage = () => { } }, 200); - window.addEventListener('scroll', fn); - return () => window.removeEventListener('scroll', fn); + window.addEventListener("scroll", fn); + return () => window.removeEventListener("scroll", fn); }, [pathname]); return <>; diff --git a/govtool/frontend/src/components/atoms/Spacer.tsx b/govtool/frontend/src/components/atoms/Spacer.tsx index f96cd43ac..7c68fd471 100644 --- a/govtool/frontend/src/components/atoms/Spacer.tsx +++ b/govtool/frontend/src/components/atoms/Spacer.tsx @@ -1,4 +1,4 @@ -import { Box } from '@mui/material'; -import { SpacerProps } from '.'; +import { Box } from "@mui/material"; +import { SpacerProps } from "."; export const Spacer = ({ x, y }: SpacerProps) => ; diff --git a/govtool/frontend/src/components/atoms/StakeRadio.tsx b/govtool/frontend/src/components/atoms/StakeRadio.tsx index 3cc89adfc..61dd45b39 100644 --- a/govtool/frontend/src/components/atoms/StakeRadio.tsx +++ b/govtool/frontend/src/components/atoms/StakeRadio.tsx @@ -18,14 +18,13 @@ type StakeRadioProps = { }; export const StakeRadio: FC = ({ ...props }) => { - const { - dataTestId, isChecked = false, stakeKey, onChange, - } = props; + const { dataTestId, isChecked = false, stakeKey, onChange } = props; const { palette: { boxShadow1 }, } = theme; const { isMobile } = useScreenDimension(); - const { powerIsLoading, votingPower } = useGetAdaHolderVotingPowerQuery(stakeKey); + const { powerIsLoading, votingPower } = + useGetAdaHolderVotingPowerQuery(stakeKey); const { t } = useTranslation(); return ( @@ -83,9 +82,7 @@ export const StakeRadio: FC = ({ ...props }) => { fontWeight={600} marginLeft="4px" > - ₳ - {" "} - {correctAdaFormat(votingPower) ?? 0} + ₳ {correctAdaFormat(votingPower) ?? 0} )} diff --git a/govtool/frontend/src/components/atoms/TextArea.tsx b/govtool/frontend/src/components/atoms/TextArea.tsx index 397f39aec..59ee06bfa 100644 --- a/govtool/frontend/src/components/atoms/TextArea.tsx +++ b/govtool/frontend/src/components/atoms/TextArea.tsx @@ -1,6 +1,4 @@ -import { - forwardRef, useCallback, useImperativeHandle, useRef, -} from "react"; +import { forwardRef, useCallback, useImperativeHandle, useRef } from "react"; import { TextareaAutosize, styled } from "@mui/material"; import { useScreenDimension } from "@hooks"; @@ -22,9 +20,7 @@ const TextAreaBase = styled(TextareaAutosize)( ); export const TextArea = forwardRef( - ({ - errorMessage, maxLength = 500, onBlur, onFocus, ...props - }, ref) => { + ({ errorMessage, maxLength = 500, onBlur, onFocus, ...props }, ref) => { const { isMobile } = useScreenDimension(); const textAraeRef = useRef(null); @@ -46,11 +42,12 @@ export const TextArea = forwardRef( useImperativeHandle( ref, - () => ({ - focus: handleFocus, - blur: handleBlur, - ...textAraeRef.current, - } as unknown as HTMLTextAreaElement), + () => + ({ + focus: handleFocus, + blur: handleBlur, + ...textAraeRef.current, + } as unknown as HTMLTextAreaElement), [handleBlur, handleFocus], ); diff --git a/govtool/frontend/src/components/atoms/Tooltip.tsx b/govtool/frontend/src/components/atoms/Tooltip.tsx index a9f881622..4b8b7a90a 100644 --- a/govtool/frontend/src/components/atoms/Tooltip.tsx +++ b/govtool/frontend/src/components/atoms/Tooltip.tsx @@ -1,13 +1,28 @@ -import { styled } from '@mui/material'; -import * as TooltipMUI from '@mui/material/Tooltip'; -import Typography from '@mui/material/Typography'; +import { styled } from "@mui/material"; +import * as TooltipMUI from "@mui/material/Tooltip"; +import Typography from "@mui/material/Typography"; -type TooltipProps = Omit & { +type TooltipProps = Omit & { heading?: string; paragraphOne?: string; paragraphTwo?: string; }; +const StyledTooltip = styled( + ({ className, ...props }: TooltipMUI.TooltipProps) => ( + // eslint-disable-next-line react/jsx-pascal-case + + ), +)(() => ({ + [`& .${TooltipMUI.tooltipClasses.arrow}`]: { + color: "rgb(36, 34, 50)", + }, + [`& .${TooltipMUI.tooltipClasses.tooltip}`]: { + backgroundColor: "rgb(36, 34, 50)", + padding: 12, + }, +})); + export const Tooltip = ({ heading, paragraphOne, @@ -18,12 +33,12 @@ export const Tooltip = ({ {...tooltipProps} enterTouchDelay={0} leaveTouchDelay={1000} - title={( + title={ <> {heading && ( - - {heading} - + + {heading} + )} {paragraphOne && paragraphOne} {paragraphTwo && ( - <> -
- {' '} -
- {paragraphTwo} - + <> +

+ {paragraphTwo} + )}
- )} + } /> ); - -const StyledTooltip = styled( - ({ className, ...props }: TooltipMUI.TooltipProps) => ( - - ), -)(() => ({ - [`& .${TooltipMUI.tooltipClasses.arrow}`]: { - color: 'rgb(36, 34, 50)', - }, - [`& .${TooltipMUI.tooltipClasses.tooltip}`]: { - backgroundColor: 'rgb(36, 34, 50)', - padding: 12, - }, -})); diff --git a/govtool/frontend/src/components/atoms/Typography.tsx b/govtool/frontend/src/components/atoms/Typography.tsx index 0c9dc75ae..34e1af742 100644 --- a/govtool/frontend/src/components/atoms/Typography.tsx +++ b/govtool/frontend/src/components/atoms/Typography.tsx @@ -1,9 +1,9 @@ -import { Typography as MUITypography } from '@mui/material'; -import { TypographyProps } from './types'; +import { Typography as MUITypography } from "@mui/material"; +import { TypographyProps } from "./types"; export const Typography = ({ color, - variant = 'body1', + variant = "body1", ...props }: TypographyProps) => { const fontSize = { @@ -33,16 +33,16 @@ export const Typography = ({ }[variant]; const lineHeight = { - headline1: '110px', - headline2: '57px', - headline3: '44px', - headline4: '40px', - headline5: '36px', - title1: '32px', - title2: '28px', - body1: '24px', - body2: '20px', - caption: '16px', + headline1: "110px", + headline2: "57px", + headline3: "44px", + headline4: "40px", + headline5: "36px", + title1: "32px", + title2: "28px", + body1: "24px", + body2: "20px", + caption: "16px", }[variant]; return ( diff --git a/govtool/frontend/src/components/atoms/VotePill.tsx b/govtool/frontend/src/components/atoms/VotePill.tsx index f2471bd10..d28ed9da4 100644 --- a/govtool/frontend/src/components/atoms/VotePill.tsx +++ b/govtool/frontend/src/components/atoms/VotePill.tsx @@ -1,5 +1,5 @@ -import { Vote } from '@models'; -import { Box, Typography } from '@mui/material'; +import { Vote } from "@models"; +import { Box, Typography } from "@mui/material"; export const VotePill = ({ vote, @@ -17,16 +17,16 @@ export const VotePill = ({ px={2.25} border={1} borderColor={ - VOTE === 'yes' ? '#C0E4BA' : VOTE === 'no' ? '#EDACAC' : '#99ADDE' + VOTE === "yes" ? "#C0E4BA" : VOTE === "no" ? "#EDACAC" : "#99ADDE" } bgcolor={ - VOTE === 'yes' ? '#F0F9EE' : VOTE === 'no' ? '#FBEBEB' : '#E6EBF7' + VOTE === "yes" ? "#F0F9EE" : VOTE === "no" ? "#FBEBEB" : "#E6EBF7" } borderRadius={100} textAlign="center" minWidth="50px" - maxWidth={maxWidth ? `${maxWidth}px` : 'auto'} - width={width ? `${width}px` : 'auto'} + maxWidth={maxWidth ? `${maxWidth}px` : "auto"} + width={width ? `${width}px` : "auto"} maxHeight="14px" > { - const { voter, stakeKey, isDrepLoading } = useCardano(); - const { dRepVotingPower, isDRepVotingPowerLoading } = - useGetDRepVotingPowerQuery(); - const { votingPower, powerIsLoading } = - useGetAdaHolderVotingPowerQuery(stakeKey); + const { stakeKey, isEnableLoading } = useCardano(); + const { dRepVotingPower } = useGetDRepVotingPowerQuery(); + const { votingPower } = useGetAdaHolderVotingPowerQuery(stakeKey); const { isMobile, screenWidth } = useScreenDimension(); const { t } = useTranslation(); + const { voter } = useGetVoterInfo(); return ( {voter?.isRegisteredAsDRep && ( @@ -51,27 +53,27 @@ export const VotingPowerChips = () => { )} {screenWidth >= 1024 && ( - {t('votingPower')} + {t("votingPower")} + + )} + {(voter?.isRegisteredAsDRep && dRepVotingPower === undefined) || + (!voter?.isRegisteredAsDRep && votingPower === undefined) || + isEnableLoading || + !voter ? ( + + ) : ( + + ₳{" "} + {voter?.isRegisteredAsDRep + ? correctAdaFormat(dRepVotingPower) ?? 0 + : correctAdaFormat(votingPower) ?? 0} )} - {(voter?.isRegisteredAsDRep && isDRepVotingPowerLoading) || - (!voter?.isRegisteredAsDRep && powerIsLoading) || - isDrepLoading ? ( - - ) : ( - - ₳ - {' '} - {voter?.isRegisteredAsDRep - ? correctAdaFormat(dRepVotingPower) ?? 0 - : correctAdaFormat(votingPower) ?? 0} - - )} ); }; diff --git a/govtool/frontend/src/components/atoms/modal/Modal.tsx b/govtool/frontend/src/components/atoms/modal/Modal.tsx index 1c4cc0722..4c88d99f8 100644 --- a/govtool/frontend/src/components/atoms/modal/Modal.tsx +++ b/govtool/frontend/src/components/atoms/modal/Modal.tsx @@ -1,10 +1,7 @@ import MuiModal from "@mui/material/Modal"; -import type { JSXElementConstructor, ReactElement } from "react"; +import type { ComponentProps } from "react"; -export type MuiModalChildren = ReactElement< - any, - string | JSXElementConstructor ->; +export type MuiModalChildren = ComponentProps["children"]; interface Props { open: boolean; @@ -14,6 +11,6 @@ interface Props { export const Modal = ({ open, children, handleClose }: Props) => ( - <>{children} + {children} ); diff --git a/govtool/frontend/src/components/atoms/modal/ModalWrapper.tsx b/govtool/frontend/src/components/atoms/modal/ModalWrapper.tsx index 9385fbaa1..598b21e4b 100644 --- a/govtool/frontend/src/components/atoms/modal/ModalWrapper.tsx +++ b/govtool/frontend/src/components/atoms/modal/ModalWrapper.tsx @@ -13,31 +13,6 @@ interface Props { sx?: SxProps; } -export const ModalWrapper = ({ - children, - onClose, - variant = "modal", - hideCloseButton = false, - dataTestId = "modal", - sx, -}: Props) => { - const { closeModal } = useModal(); - - return ( - - {variant !== "popup" && !hideCloseButton && ( - - )} - {children} - - ); -}; - export const BaseWrapper = styled("div")>` box-shadow: 1px 2px 11px 0px #00123d5e; max-height: 90vh; @@ -73,3 +48,28 @@ export const CloseButton = styled("img")` top: 24px; right: 24px; `; + +export const ModalWrapper = ({ + children, + onClose, + variant = "modal", + hideCloseButton = false, + dataTestId = "modal", + sx, +}: Props) => { + const { closeModal } = useModal(); + + return ( + + {variant !== "popup" && !hideCloseButton && ( + + )} + {children} + + ); +}; diff --git a/govtool/frontend/src/components/molecules/ActionCard.tsx b/govtool/frontend/src/components/molecules/ActionCard.tsx index 525507176..6cab992d7 100644 --- a/govtool/frontend/src/components/molecules/ActionCard.tsx +++ b/govtool/frontend/src/components/molecules/ActionCard.tsx @@ -52,7 +52,12 @@ export const ActionCard: FC = ({ ...props }) => { > {imageURL ? ( - + action-card ) : null} {title ? ( colors.backgroundColor ?? `${theme.palette.neutralWhite}4D`, + backgroundColor: (theme) => + colors.backgroundColor ?? `${theme.palette.neutralWhite}4D`, border: border ? 1 : 0, borderColor: colors?.borderColor, padding: 3, @@ -51,26 +73,3 @@ export const Card = ({ ); }; - -const COLORS = { - default: { - backgroundColor: undefined, - borderColor: primaryBlue.c100, - }, - warning: { - backgroundColor: undefined, - borderColor: orange.c500, - }, - error: { - backgroundColor: `${errorRed.c50}80`, - borderColor: errorRed.c100, - }, - primary: { - backgroundColor: `${primaryBlue.c100}40`, - borderColor: primaryBlue.c500, - }, - success: { - backgroundColor: undefined, - borderColor: successGreen.c500, - }, -} as const; diff --git a/govtool/frontend/src/components/molecules/CenteredBoxBottomButtons.tsx b/govtool/frontend/src/components/molecules/CenteredBoxBottomButtons.tsx index 0169c8ad5..101787797 100644 --- a/govtool/frontend/src/components/molecules/CenteredBoxBottomButtons.tsx +++ b/govtool/frontend/src/components/molecules/CenteredBoxBottomButtons.tsx @@ -1,8 +1,8 @@ -import { useMemo } from 'react'; -import { Box } from '@mui/material'; +import { useMemo } from "react"; +import { Box } from "@mui/material"; -import { Button, LoadingButton } from '@atoms'; -import { useScreenDimension, useTranslation } from '@hooks'; +import { Button, LoadingButton } from "@atoms"; +import { useScreenDimension, useTranslation } from "@hooks"; interface Props { onBackButton: () => void; @@ -33,7 +33,7 @@ export const CenteredBoxBottomButtons = ({ }} variant="outlined" > - {backButtonText ?? t('cancel')} + {backButtonText ?? t("cancel")} ), [isMobile], @@ -52,7 +52,7 @@ export const CenteredBoxBottomButtons = ({ }} variant="contained" > - {actionButtonText ?? t('continue')} + {actionButtonText ?? t("continue")} ), [isLoading, isMobile], @@ -61,7 +61,7 @@ export const CenteredBoxBottomButtons = ({ return ( diff --git a/govtool/frontend/src/components/molecules/DRepInfoCard.tsx b/govtool/frontend/src/components/molecules/DRepInfoCard.tsx index 95d90230c..f01d31897 100644 --- a/govtool/frontend/src/components/molecules/DRepInfoCard.tsx +++ b/govtool/frontend/src/components/molecules/DRepInfoCard.tsx @@ -1,8 +1,8 @@ -import { Box, Typography } from '@mui/material'; +import { Box, Typography } from "@mui/material"; -import { useCardano } from '@context'; -import { CopyButton } from '@atoms'; -import { useTranslation } from '@hooks'; +import { useCardano } from "@context"; +import { CopyButton } from "@atoms"; +import { useTranslation } from "@hooks"; export const DRepInfoCard = () => { const { dRepIDBech32 } = useCardano(); @@ -10,9 +10,9 @@ export const DRepInfoCard = () => { return ( - + - {t('myDRepId')} + {t("myDRepId")} diff --git a/govtool/frontend/src/components/molecules/DashboardActionCard.tsx b/govtool/frontend/src/components/molecules/DashboardActionCard.tsx index b1805672f..29d9cd940 100644 --- a/govtool/frontend/src/components/molecules/DashboardActionCard.tsx +++ b/govtool/frontend/src/components/molecules/DashboardActionCard.tsx @@ -100,6 +100,7 @@ export const DashboardActionCard: FC = ({ /> ) : ( card = ({ screenWidth < 375 ? "150px" : screenWidth < 425 - ? "200px" - : screenWidth < 768 - ? "240px" - : screenWidth < 1024 - ? "300px" - : screenWidth < 1440 - ? "150px" - : "300px", + ? "200px" + : screenWidth < 768 + ? "240px" + : screenWidth < 1024 + ? "300px" + : screenWidth < 1440 + ? "150px" + : "300px", }} > {cardId} diff --git a/govtool/frontend/src/components/molecules/DataActionsBar.tsx b/govtool/frontend/src/components/molecules/DataActionsBar.tsx index f92a6eaee..86613a8bc 100644 --- a/govtool/frontend/src/components/molecules/DataActionsBar.tsx +++ b/govtool/frontend/src/components/molecules/DataActionsBar.tsx @@ -56,7 +56,7 @@ export const DataActionsBar: FC = ({ ...props }) => { onChange={(e) => setSearchText(e.target.value)} placeholder="Search..." value={searchText} - startAdornment={( + startAdornment={ = ({ ...props }) => { width: 16, }} /> - )} + } sx={{ bgcolor: "white", border: 1, diff --git a/govtool/frontend/src/components/molecules/Field/Checkbox.tsx b/govtool/frontend/src/components/molecules/Field/Checkbox.tsx index 7ab257e51..2495ca810 100644 --- a/govtool/frontend/src/components/molecules/Field/Checkbox.tsx +++ b/govtool/frontend/src/components/molecules/Field/Checkbox.tsx @@ -1,9 +1,9 @@ -import { Box } from '@mui/material'; +import { Box } from "@mui/material"; -import { Checkbox as CheckboxBase, FormErrorMessage, Typography } from '@atoms'; +import { Checkbox as CheckboxBase, FormErrorMessage, Typography } from "@atoms"; -import { useCallback } from 'react'; -import { CheckboxFieldProps } from './types'; +import { useCallback } from "react"; +import { CheckboxFieldProps } from "./types"; export const Checkbox = ({ errorMessage, @@ -20,15 +20,15 @@ export const Checkbox = ({ }, [value]); return ( - + ( useImperativeHandle( ref, - () => ({ - focus: handleFocus, - blur: handleBlur, - ...inputRef.current, - } as unknown as HTMLInputElement), + () => + ({ + focus: handleFocus, + blur: handleBlur, + ...inputRef.current, + } as unknown as HTMLInputElement), [handleBlur, handleFocus], ); diff --git a/govtool/frontend/src/components/molecules/Field/TextArea.tsx b/govtool/frontend/src/components/molecules/Field/TextArea.tsx index 20956a5b8..20688cbe8 100644 --- a/govtool/frontend/src/components/molecules/Field/TextArea.tsx +++ b/govtool/frontend/src/components/molecules/Field/TextArea.tsx @@ -7,9 +7,7 @@ import { Typography, } from "@atoms"; -import { - forwardRef, useCallback, useImperativeHandle, useRef, -} from "react"; +import { forwardRef, useCallback, useImperativeHandle, useRef } from "react"; import { TextAreaFieldProps } from "./types"; export const TextArea = forwardRef( @@ -49,11 +47,12 @@ export const TextArea = forwardRef( useImperativeHandle( ref, - () => ({ - focus: handleFocus, - blur: handleBlur, - ...textAreaRef.current, - } as unknown as HTMLTextAreaElement), + () => + ({ + focus: handleFocus, + blur: handleBlur, + ...textAreaRef.current, + } as unknown as HTMLTextAreaElement), [handleBlur, handleFocus], ); return ( @@ -99,9 +98,7 @@ export const TextArea = forwardRef( }} variant="caption" > - {props?.value?.toString()?.length ?? 0} - / - {maxLength} + {props?.value?.toString()?.length ?? 0}/{maxLength} ); diff --git a/govtool/frontend/src/components/molecules/Field/index.tsx b/govtool/frontend/src/components/molecules/Field/index.tsx index e02c0ecec..4065b89fa 100644 --- a/govtool/frontend/src/components/molecules/Field/index.tsx +++ b/govtool/frontend/src/components/molecules/Field/index.tsx @@ -1,8 +1,8 @@ -import React, { PropsWithChildren } from 'react'; +import React, { PropsWithChildren } from "react"; -import { Checkbox } from './Checkbox'; -import { Input } from './Input'; -import { TextArea } from './TextArea'; +import { Checkbox } from "./Checkbox"; +import { Input } from "./Input"; +import { TextArea } from "./TextArea"; type FieldComposition = React.FC & { Input: typeof Input; @@ -18,4 +18,4 @@ Field.TextArea = TextArea; export { Field }; -export * from './types'; +export * from "./types"; diff --git a/govtool/frontend/src/components/molecules/GovActionDetails.tsx b/govtool/frontend/src/components/molecules/GovActionDetails.tsx index ff32ef6c0..87fdf76b8 100644 --- a/govtool/frontend/src/components/molecules/GovActionDetails.tsx +++ b/govtool/frontend/src/components/molecules/GovActionDetails.tsx @@ -1,24 +1,24 @@ -import { Typography } from '../atoms'; +import { Typography } from "../atoms"; export const GovActionDetails = ({ title, value, }: { title: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any value: any; }) => { if (Array.isArray(value)) { return (
- - {title} - : - + {title}:
    {value.map((item, index) => ( + // TODO: Get rid of index as key - ref: https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-array-index-key.md + // eslint-disable-next-line react/no-array-index-key
  • {item} @@ -29,26 +29,22 @@ export const GovActionDetails = ({
); } - if (typeof value === 'boolean') { + if (typeof value === "boolean") { return ( - {title} - : - {value ? 'True' : 'False'} + {title}:{value ? "True" : "False"} ); } return ( - {title} - : - {value} + {title}:{value} ); }; diff --git a/govtool/frontend/src/components/molecules/GovernanceActionsFilters.tsx b/govtool/frontend/src/components/molecules/GovernanceActionsFilters.tsx index f0118354f..05746f111 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionsFilters.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionsFilters.tsx @@ -1,14 +1,14 @@ -import { Dispatch, SetStateAction, useCallback } from 'react'; +import { Dispatch, SetStateAction, useCallback } from "react"; import { Box, Checkbox, FormControlLabel, FormLabel, Typography, -} from '@mui/material'; +} from "@mui/material"; -import { GOVERNANCE_ACTIONS_FILTERS } from '@consts'; -import { useTranslation } from '@hooks'; +import { GOVERNANCE_ACTIONS_FILTERS } from "@consts"; +import { useTranslation } from "@hooks"; interface Props { chosenFilters: string[]; @@ -21,6 +21,8 @@ export const GovernanceActionsFilters = ({ }: Props) => { const handleFilterChange = useCallback( (e: React.ChangeEvent) => { + // TODO: Refine if it is needed to remove this eslint-disable + // eslint-disable-next-line no-unused-expressions, no-sequences e.target.name, e.target.checked; let filters = [...chosenFilters]; if (e.target.checked) { @@ -41,49 +43,51 @@ export const GovernanceActionsFilters = ({ flexDirection="column" position="absolute" sx={{ - background: '#FBFBFF', - boxShadow: '1px 2px 11px 0px #00123D5E', - borderRadius: '10px', - padding: '12px 0px', - width: 'auto', - zIndex: '1', + background: "#FBFBFF", + boxShadow: "1px 2px 11px 0px #00123D5E", + borderRadius: "10px", + padding: "12px 0px", + width: "auto", + zIndex: "1", }} > - {t('govActions.filterTitle')} + {t("govActions.filterTitle")} {GOVERNANCE_ACTIONS_FILTERS.map((item) => ( - )} - label={( + } + label={ {item.label} - )} + } /> ))} diff --git a/govtool/frontend/src/components/molecules/GovernanceActionsSorting.tsx b/govtool/frontend/src/components/molecules/GovernanceActionsSorting.tsx index 8c6965281..95fa648db 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionsSorting.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionsSorting.tsx @@ -1,4 +1,4 @@ -import { Dispatch, SetStateAction } from 'react'; +import { Dispatch, SetStateAction } from "react"; import { Box, FormControl, @@ -6,10 +6,10 @@ import { Radio, RadioGroup, Typography, -} from '@mui/material'; +} from "@mui/material"; -import { GOVERNANCE_ACTIONS_SORTING } from '@consts'; -import { useTranslation } from '@hooks'; +import { GOVERNANCE_ACTIONS_SORTING } from "@consts"; +import { useTranslation } from "@hooks"; interface Props { chosenSorting: string; @@ -28,22 +28,22 @@ export const GovernanceActionsSorting = ({ flexDirection="column" position="absolute" sx={{ - background: '#FBFBFF', - boxShadow: '1px 2px 11px 0px #00123D5E', - borderRadius: '10px', - padding: '12px 0px', - width: 'auto', - zIndex: '1', + background: "#FBFBFF", + boxShadow: "1px 2px 11px 0px #00123D5E", + borderRadius: "10px", + padding: "12px 0px", + width: "auto", + zIndex: "1", }} > - - {t('sortBy')} + + {t("sortBy")} - setChosenSorting('')}> + setChosenSorting("")}> - {t('clear')} + {t("clear")} @@ -60,16 +60,19 @@ export const GovernanceActionsSorting = ({ sx={[ { margin: 0, - px: '20px', + px: "20px", bgcolor: - chosenSorting === item.key ? '#FFF0E7' : 'transparent', + chosenSorting === item.key ? "#FFF0E7" : "transparent", }, - { '&:hover': { bgcolor: '#E6EBF7' } }, + { "&:hover": { bgcolor: "#E6EBF7" } }, ]} key={item.key} value={item.key} control={ - + // TODO: Fix typing of inputProps + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + } label={item.label} /> diff --git a/govtool/frontend/src/components/molecules/GovernanceVotedOnCard.tsx b/govtool/frontend/src/components/molecules/GovernanceVotedOnCard.tsx index ef234df65..817c8495d 100644 --- a/govtool/frontend/src/components/molecules/GovernanceVotedOnCard.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceVotedOnCard.tsx @@ -1,26 +1,23 @@ -import { useNavigate } from 'react-router-dom'; -import { Box } from '@mui/material'; -import CheckIcon from '@mui/icons-material/Check'; -import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'; +import { useNavigate } from "react-router-dom"; +import { Box } from "@mui/material"; +import CheckIcon from "@mui/icons-material/Check"; +import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; -import { - Button, VotePill, Typography, Tooltip -} from '@atoms'; -import { PATHS } from '@consts'; -import { useScreenDimension, useTranslation } from '@hooks'; -import { VotedProposal } from '@models'; +import { Button, VotePill, Typography, Tooltip } from "@atoms"; +import { PATHS } from "@consts"; +import { useScreenDimension, useTranslation } from "@hooks"; +import { VotedProposal } from "@models"; import { formatDisplayDate, getFullGovActionId, getProposalTypeLabel, getShortenedGovActionId, openInNewTab, -} from '@utils'; -import { theme } from '@/theme'; +} from "@utils"; +import { theme } from "@/theme"; interface Props { votedProposal: VotedProposal; - searchPhrase?: string; inProgress?: boolean; } @@ -35,7 +32,7 @@ export const GovernanceVotedOnCard = ({ votedProposal, inProgress }: Props) => { const proposalTypeNoEmptySpaces = getProposalTypeLabel(proposal.type).replace( / /g, - '', + "", ); return ( @@ -45,44 +42,44 @@ export const GovernanceVotedOnCard = ({ votedProposal, inProgress }: Props) => { maxWidth={402} minWidth={isMobile ? 295 : 402} sx={{ - backgroundColor: 'transparent', - borderRadius: '20px', + backgroundColor: "transparent", + borderRadius: "20px", boxShadow: inProgress - ? '2px 2px 20px 0px #F55A0033' - : '0px 4px 15px 0px #DDE3F5', - position: 'relative', + ? "2px 2px 20px 0px #F55A0033" + : "0px 4px 15px 0px #DDE3F5", + position: "relative", }} data-testid={`govaction-${proposalTypeNoEmptySpaces}-voted-on-card`} > {inProgress ? ( - t('inProgress') + t("inProgress") ) : ( <> - {t('govActions.voteSubmitted')} + {t("govActions.voteSubmitted")} )} @@ -94,12 +91,12 @@ export const GovernanceVotedOnCard = ({ votedProposal, inProgress }: Props) => { sx={{ borderTopLeftRadius: 20, borderTopRightRadius: 20, - backgroundColor: 'rgba(255, 255, 255, 0.3)', + backgroundColor: "rgba(255, 255, 255, 0.3)", }} > - {t('govActions.governanceActionType')} + {t("govActions.governanceActionType")} { - {t('govActions.governanceActionId')} + {t("govActions.governanceActionId")} { - {t('govActions.myVote')} + {t("govActions.myVote")} { @@ -187,20 +185,20 @@ export const GovernanceVotedOnCard = ({ votedProposal, inProgress }: Props) => { py={0.75} > - {t('govActions.submissionDate')} + {t("govActions.submissionDate")} {formatDisplayDate(proposal.createdDate)} { py={0.75} > - {t('govActions.expiryDate')} + {t("govActions.expiryDate")} {formatDisplayDate(proposal.expiryDate)} { onClick={() => navigate( PATHS.dashboardGovernanceActionsAction.replace( - ':proposalId', + ":proposalId", getFullGovActionId(proposal.txHash, proposal.index), ), { @@ -265,14 +263,15 @@ export const GovernanceVotedOnCard = ({ votedProposal, inProgress }: Props) => { vote: vote.vote.toLowerCase(), }, }, - )} + ) + } sx={{ - backgroundColor: '#FBFBFF', - width: '100%', + backgroundColor: "#FBFBFF", + width: "100%", }} variant="outlined" > - {t('govActions.changeYourVote')} + {t("govActions.changeYourVote")} diff --git a/govtool/frontend/src/components/molecules/LinkWithIcon.tsx b/govtool/frontend/src/components/molecules/LinkWithIcon.tsx index 51a8083ac..ab12e106c 100644 --- a/govtool/frontend/src/components/molecules/LinkWithIcon.tsx +++ b/govtool/frontend/src/components/molecules/LinkWithIcon.tsx @@ -1,9 +1,9 @@ -import { Box } from '@mui/material'; -import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos'; +import { Box } from "@mui/material"; +import ArrowBackIosIcon from "@mui/icons-material/ArrowBackIos"; -import { Typography } from '@atoms'; +import { Typography } from "@atoms"; -import { LinkWithIconProps } from './types'; +import { LinkWithIconProps } from "./types"; export const LinkWithIcon = ({ label, @@ -12,12 +12,12 @@ export const LinkWithIcon = ({ sx, }: LinkWithIconProps) => ( { }} src={filtersOpen ? ICONS.filterWhiteIcon : ICONS.filterIcon} style={{ - background: filtersOpen ? secondary.main : 'transparent', - borderRadius: '100%', - cursor: 'pointer', - padding: '14px', - overflow: 'visible', + background: filtersOpen ? secondary.main : "transparent", + borderRadius: "100%", + cursor: "pointer", + padding: "14px", + overflow: "visible", height: 20, width: 20, - objectFit: 'contain', + objectFit: "contain", }} /> {!filtersOpen && chosenFiltersLength > 0 && ( @@ -89,30 +89,30 @@ export const OrderActionsChip = (props: Props) => { }} src={sortOpen ? ICONS.sortWhiteIcon : ICONS.sortIcon} style={{ - background: sortOpen ? secondary.main : 'transparent', - borderRadius: '100%', - cursor: 'pointer', - padding: '14px', + background: sortOpen ? secondary.main : "transparent", + borderRadius: "100%", + cursor: "pointer", + padding: "14px", height: 24, width: 24, - objectFit: 'contain', + objectFit: "contain", }} /> {!sortOpen && sortingActive && ( sorting active diff --git a/govtool/frontend/src/components/molecules/Step.tsx b/govtool/frontend/src/components/molecules/Step.tsx index 7478f46f8..7b8e1a0b4 100644 --- a/govtool/frontend/src/components/molecules/Step.tsx +++ b/govtool/frontend/src/components/molecules/Step.tsx @@ -1,12 +1,13 @@ -import { Box } from '@mui/material'; +import { Box } from "@mui/material"; -import { Typography } from '@atoms'; -import { theme } from '@/theme'; +import { Typography } from "@atoms"; +import { theme } from "@/theme"; -import { StepProps } from './types'; +import { StepProps } from "./types"; export const Step = ({ component, + componentsLayoutStyles, label, layoutStyles, stepNumber, @@ -18,20 +19,20 @@ export const Step = ({ return ( @@ -42,13 +43,14 @@ export const Step = ({ - + {label} {component} diff --git a/govtool/frontend/src/components/molecules/VoteActionForm.tsx b/govtool/frontend/src/components/molecules/VoteActionForm.tsx index 3a21e022a..4c7413400 100644 --- a/govtool/frontend/src/components/molecules/VoteActionForm.tsx +++ b/govtool/frontend/src/components/molecules/VoteActionForm.tsx @@ -1,18 +1,19 @@ -import { - useState, useEffect, useMemo, useCallback -} from 'react'; -import { useLocation } from 'react-router-dom'; -import { Box, Link } from '@mui/material'; +import { useState, useEffect, useMemo, useCallback } from "react"; +import { useLocation } from "react-router-dom"; +import { Box, Link } from "@mui/material"; +import { Button, LoadingButton, Radio, Spacer, Typography } from "@atoms"; +import { ICONS } from "@consts"; +import { useModal } from "@context"; import { - Button, LoadingButton, Radio, Spacer, Typography -} from '@atoms'; -import { ICONS } from '@consts'; -import { useCardano, useModal } from '@context'; -import { useScreenDimension, useVoteActionForm, useTranslation } from '@hooks'; -import { openInNewTab } from '@utils'; + useScreenDimension, + useVoteActionForm, + useTranslation, + useGetVoterInfo, +} from "@hooks"; +import { openInNewTab } from "@utils"; -import { ControlledField } from '../organisms'; +import { ControlledField } from "../organisms"; export const VoteActionForm = ({ voteFromEP, @@ -29,8 +30,8 @@ export const VoteActionForm = ({ const [isContext, setIsContext] = useState(false); const { isMobile, screenWidth } = useScreenDimension(); const { openModal } = useModal(); - const { voter } = useCardano(); const { t } = useTranslation(); + const { voter } = useGetVoterInfo(); const { areFormErrors, @@ -47,9 +48,9 @@ export const VoteActionForm = ({ useEffect(() => { if (state && state.vote) { - setValue('vote', state.vote); + setValue("vote", state.vote); } else if (voteFromEP) { - setValue('vote', voteFromEP); + setValue("vote", voteFromEP); } }, [state, voteFromEP, setValue]); @@ -65,41 +66,40 @@ export const VoteActionForm = ({ () => ( ), [state], ); - const renderChangeVoteButton = useMemo( - () => ( - - {t('govActions.changeVote')} - - ), + const renderChangeVoteButton = useMemo(() => ( + + {t('govActions.changeVote')} + + ), [confirmVote, areFormErrors, vote, isVoteLoading], ); @@ -112,38 +112,38 @@ export const VoteActionForm = ({ px={screenWidth < 1024 ? 0 : 5} > - {t('govActions.chooseHowToVote')} + {t("govActions.chooseHowToVote")} @@ -156,7 +156,7 @@ export const VoteActionForm = ({ sx={{ mt: 3 }} onClick={() => { openModal({ - type: 'votingPower', + type: "votingPower", state: { yesVotes, noVotes, @@ -166,7 +166,7 @@ export const VoteActionForm = ({ }); }} > - {t('govActions.showVotes')} + {t("govActions.showVotes")} )}

- {t('govActions.provideContext')} - {' '} + {t("govActions.provideContext")}{" "} - {t('govActions.optional')} + {t("govActions.optional")} arrow @@ -212,29 +211,30 @@ export const VoteActionForm = ({ {...{ control, errors }} dataTestId="url-input" name="url" - placeholder={t('forms.urlWithContextPlaceholder')} + placeholder={t("forms.urlWithContextPlaceholder")} /> openInNewTab( - 'https://docs.sanchogov.tools/faqs/how-to-create-a-metadata-anchor', - )} + "https://docs.sanchogov.tools/faqs/how-to-create-a-metadata-anchor", + ) + } mb={isMobile ? 2 : 8} - sx={{ cursor: 'pointer' }} + sx={{ cursor: "pointer" }} textAlign="center" - visibility={!isContext ? 'hidden' : 'visible'} + visibility={!isContext ? "hidden" : "visible"} > - {t('forms.howCreateUrlAndHash')} + {t("forms.howCreateUrlAndHash")} @@ -243,41 +243,41 @@ export const VoteActionForm = ({ - {t('govActions.selectDifferentOption')} + {t("govActions.selectDifferentOption")} {(state?.vote && state?.vote !== vote) || - (voteFromEP && voteFromEP !== vote) ? ( - - {isMobile ? renderChangeVoteButton : renderCancelButton} - - {isMobile ? renderCancelButton : renderChangeVoteButton} - - ) : ( - + {isMobile ? renderChangeVoteButton : renderCancelButton} + + {isMobile ? renderCancelButton : renderChangeVoteButton} + + ) : ( + - {t('govActions.vote')} - - )} + isLoading={isVoteLoading} + onClick={confirmVote} + size="extraLarge" + > + {t("govActions.vote")} + + )} ); }; diff --git a/govtool/frontend/src/components/molecules/VotesSubmitted.tsx b/govtool/frontend/src/components/molecules/VotesSubmitted.tsx index 7085786ce..8115c8fac 100644 --- a/govtool/frontend/src/components/molecules/VotesSubmitted.tsx +++ b/govtool/frontend/src/components/molecules/VotesSubmitted.tsx @@ -1,10 +1,10 @@ -import { Box, Typography } from '@mui/material'; +import { Box, Typography } from "@mui/material"; -import { VotePill } from '@atoms'; -import { useScreenDimension, useTranslation } from '@hooks'; -import { theme } from '@/theme'; -import { IMAGES } from '@/consts'; -import { correctAdaFormat } from '@/utils/adaFormat'; +import { VotePill } from "@atoms"; +import { useScreenDimension, useTranslation } from "@hooks"; +import { theme } from "@/theme"; +import { IMAGES } from "@/consts"; +import { correctAdaFormat } from "@/utils/adaFormat"; interface Props { yesVotes: number; @@ -22,8 +22,8 @@ export const VotesSubmitted = ({ yesVotes, noVotes, abstainVotes }: Props) => { return ( { src={IMAGES.govActionListImage} width="64px" height="64px" - style={{ marginBottom: '10px' }} + style={{ marginBottom: "10px" }} /> - {t('govActions.voteSubmitted')} + {t("govActions.voteSubmitted")} - {t('govActions.forGovAction')} + {t("govActions.forGovAction")} - - {t('govActions.votesSubmittedOnChain')} + + {t("govActions.votesSubmittedOnChain")} - {t('govActions.votes')} + {t("govActions.votes")} - ₳ - {' '} - {correctAdaFormat(yesVotes)} + ₳ {correctAdaFormat(yesVotes)} - ₳ - {' '} - {correctAdaFormat(abstainVotes)} + ₳ {correctAdaFormat(abstainVotes)} - + - ₳ - {' '} - {correctAdaFormat(noVotes)} + ₳ {correctAdaFormat(noVotes)} diff --git a/govtool/frontend/src/components/molecules/WalletInfoCard.tsx b/govtool/frontend/src/components/molecules/WalletInfoCard.tsx index f2453f67b..d45dd3e31 100644 --- a/govtool/frontend/src/components/molecules/WalletInfoCard.tsx +++ b/govtool/frontend/src/components/molecules/WalletInfoCard.tsx @@ -1,9 +1,9 @@ -import { useNavigate } from 'react-router-dom'; -import { Box, Button, Typography } from '@mui/material'; +import { useNavigate } from "react-router-dom"; +import { Box, Button, Typography } from "@mui/material"; -import { PATHS } from '@consts'; -import { useCardano } from '@context'; -import { useTranslation } from '@hooks'; +import { PATHS } from "@consts"; +import { useCardano } from "@context"; +import { useTranslation } from "@hooks"; export const WalletInfoCard = () => { const { address, disconnectWallet } = useCardano(); @@ -21,24 +21,24 @@ export const WalletInfoCard = () => { - - {t('wallet.connectedWallet')} + + {t("wallet.connectedWallet")} - + @@ -49,7 +49,7 @@ export const WalletInfoCard = () => { variant="text" onClick={onClickDisconnect} > - {t('wallet.disconnect')} + {t("wallet.disconnect")} diff --git a/govtool/frontend/src/components/molecules/WalletOption.tsx b/govtool/frontend/src/components/molecules/WalletOption.tsx index 081bd6c04..1b60e6dc7 100644 --- a/govtool/frontend/src/components/molecules/WalletOption.tsx +++ b/govtool/frontend/src/components/molecules/WalletOption.tsx @@ -21,9 +21,7 @@ export const WalletOptionButton: FC = ({ ...props }) => { } = theme; const navigate = useNavigate(); - const { - dataTestId, icon, label, name, cip95Available, - } = props; + const { dataTestId, icon, label, name, cip95Available } = props; const enableByWalletName = useCallback(async () => { if (isEnableLoading) return; @@ -60,8 +58,8 @@ export const WalletOptionButton: FC = ({ ...props }) => { "&:hover": isEnableLoading ? undefined : { - background: lightBlue, - }, + background: lightBlue, + }, }} key={name} onClick={enableByWalletName} diff --git a/govtool/frontend/src/components/molecules/types.ts b/govtool/frontend/src/components/molecules/types.ts index 5e817df41..0ed7df185 100644 --- a/govtool/frontend/src/components/molecules/types.ts +++ b/govtool/frontend/src/components/molecules/types.ts @@ -8,8 +8,9 @@ export type LinkWithIconProps = { }; export type StepProps = { - component: JSX.Element; label: string; - layoutStyles?: SxProps; stepNumber: number | string; + component?: React.ReactNode; + componentsLayoutStyles?: SxProps; + layoutStyles?: SxProps; }; diff --git a/govtool/frontend/src/components/organisms/BgCard.tsx b/govtool/frontend/src/components/organisms/BgCard.tsx index 4ab92c514..748674365 100644 --- a/govtool/frontend/src/components/organisms/BgCard.tsx +++ b/govtool/frontend/src/components/organisms/BgCard.tsx @@ -1,13 +1,13 @@ -import { useCallback, useMemo } from 'react'; -import { useNavigate } from 'react-router-dom'; -import { Box } from '@mui/material'; +import { useCallback, useMemo } from "react"; +import { useNavigate } from "react-router-dom"; +import { Box } from "@mui/material"; -import { Button, LoadingButton } from '@atoms'; -import { PATHS } from '@consts'; -import { useScreenDimension, useTranslation } from '@hooks'; -import { theme } from '@/theme'; +import { Button, LoadingButton } from "@atoms"; +import { PATHS } from "@consts"; +import { useScreenDimension, useTranslation } from "@hooks"; +import { theme } from "@/theme"; -import { BgCardProps } from './types'; +import { BgCardProps } from "./types"; export const BgCard = ({ actionButtonLabel, @@ -42,7 +42,7 @@ export const BgCard = ({ }} variant="outlined" > - {backButtonLabel ?? t('back')} + {backButtonLabel ?? t("back")} ), [isMobile], @@ -75,40 +75,40 @@ export const BgCard = ({ return ( = 768 ? 'center' : 'inherit', - display: 'flex', + alignItems: screenWidth >= 768 ? "center" : "inherit", + display: "flex", flex: 1, - flexDirection: 'column', - height: isMobile ? '100%' : 'auto', + flexDirection: "column", + height: isMobile ? "100%" : "auto", px: isMobile ? 0 : 5, }} > 768 ? 600 : undefined, mb: isMobile ? undefined : 3, pb: isMobile ? undefined : 10, pt: isMobile ? 6 : 10, px: isMobile ? 2 : 18.75, - width: '-webkit-fill-available', + width: "-webkit-fill-available", ...sx, }} > - + {children} {renderBackButton} diff --git a/govtool/frontend/src/components/organisms/ChooseStakeKeyPanel.tsx b/govtool/frontend/src/components/organisms/ChooseStakeKeyPanel.tsx index 63701dae0..893a5fa94 100644 --- a/govtool/frontend/src/components/organisms/ChooseStakeKeyPanel.tsx +++ b/govtool/frontend/src/components/organisms/ChooseStakeKeyPanel.tsx @@ -1,8 +1,6 @@ import { useMemo, useState } from "react"; import { useNavigate } from "react-router-dom"; -import { - Box, Button, Grid, Typography, -} from "@mui/material"; +import { Box, Button, Grid, Typography } from "@mui/material"; import { StakeRadio } from "@atoms"; import { useCardano, useSnackbar } from "@context"; @@ -22,48 +20,54 @@ export const ChooseStakeKeyPanel = () => { palette: { boxShadow2 }, } = theme; - const renderCancelButton = useMemo(() => ( - - ), [isMobile]); + const renderCancelButton = useMemo( + () => ( + + ), + [isMobile], + ); - const renderSelectButton = useMemo(() => ( - - ), [isMobile, chosenKey, setStakeKey]); + const renderSelectButton = useMemo( + () => ( + + ), + [isMobile, chosenKey, setStakeKey], + ); return ( { {t("wallet.noWalletsToConnect")} ) : ( - walletOptions.map(({ - icon, label, name, cip95Available, - }) => ( + walletOptions.map(({ icon, label, name, cip95Available }) => ( { openInNewTab( - "https://docs.sanchogov.tools/how-to-use-the-govtool/getting-started/get-a-compatible-wallet", - )} + onClick={() => + openInNewTab( + "https://docs.sanchogov.tools/how-to-use-the-govtool/getting-started/get-a-compatible-wallet", + ) + } sx={{ cursor: "pointer" }} > {t("here")} diff --git a/govtool/frontend/src/components/organisms/ControlledField/Checkbox.tsx b/govtool/frontend/src/components/organisms/ControlledField/Checkbox.tsx index a78c4994e..f04ec73dc 100644 --- a/govtool/frontend/src/components/organisms/ControlledField/Checkbox.tsx +++ b/govtool/frontend/src/components/organisms/ControlledField/Checkbox.tsx @@ -1,9 +1,9 @@ -import { useCallback } from 'react'; -import { Controller, get } from 'react-hook-form'; +import { useCallback } from "react"; +import { Controller, get } from "react-hook-form"; -import { Field } from '@molecules'; +import { Field } from "@molecules"; -import { ControlledCheckboxProps, RenderInputProps } from './types'; +import { ControlledCheckboxProps, RenderInputProps } from "./types"; export const Checkbox = ({ control, diff --git a/govtool/frontend/src/components/organisms/ControlledField/Input.tsx b/govtool/frontend/src/components/organisms/ControlledField/Input.tsx index 03d6b00c5..20b594425 100644 --- a/govtool/frontend/src/components/organisms/ControlledField/Input.tsx +++ b/govtool/frontend/src/components/organisms/ControlledField/Input.tsx @@ -6,9 +6,7 @@ import { Field } from "@molecules"; import { ControlledInputProps, RenderInputProps } from "./types"; export const Input = forwardRef( - ({ - control, name, errors, rules, ...props - }, ref) => { + ({ control, name, errors, rules, ...props }, ref) => { const errorMessage = get(errors, name)?.message as string; const renderInput = useCallback( diff --git a/govtool/frontend/src/components/organisms/ControlledField/TextArea.tsx b/govtool/frontend/src/components/organisms/ControlledField/TextArea.tsx index e0d15c479..9930c3a21 100644 --- a/govtool/frontend/src/components/organisms/ControlledField/TextArea.tsx +++ b/govtool/frontend/src/components/organisms/ControlledField/TextArea.tsx @@ -1,9 +1,9 @@ -import { useCallback } from 'react'; -import { Controller, get } from 'react-hook-form'; +import { useCallback } from "react"; +import { Controller, get } from "react-hook-form"; -import { Field } from '@molecules'; +import { Field } from "@molecules"; -import { ControlledTextAreaProps, RenderInputProps } from './types'; +import { ControlledTextAreaProps, RenderInputProps } from "./types"; export const TextArea = ({ control, diff --git a/govtool/frontend/src/components/organisms/ControlledField/index.tsx b/govtool/frontend/src/components/organisms/ControlledField/index.tsx index 1131b229d..9962deec7 100644 --- a/govtool/frontend/src/components/organisms/ControlledField/index.tsx +++ b/govtool/frontend/src/components/organisms/ControlledField/index.tsx @@ -10,7 +10,9 @@ type ControlledFieldComposition = React.FC & { TextArea: typeof TextArea; }; -const ControlledField: ControlledFieldComposition = ({ children }) => <>{children}; +const ControlledField: ControlledFieldComposition = ({ children }) => ( + <>{children} +); ControlledField.Checkbox = Checkbox; ControlledField.Input = Input; diff --git a/govtool/frontend/src/components/organisms/ControlledField/types.ts b/govtool/frontend/src/components/organisms/ControlledField/types.ts index 6bf85727d..c20302a8f 100644 --- a/govtool/frontend/src/components/organisms/ControlledField/types.ts +++ b/govtool/frontend/src/components/organisms/ControlledField/types.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { CheckboxFieldProps, InputFieldProps, diff --git a/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/ChooseGovernanceActionType.tsx b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/ChooseGovernanceActionType.tsx index bd5f110ee..49dd7af0d 100644 --- a/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/ChooseGovernanceActionType.tsx +++ b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/ChooseGovernanceActionType.tsx @@ -1,13 +1,13 @@ -import { Dispatch, SetStateAction } from 'react'; -import { ActionRadio, Spacer, Typography } from '@atoms'; +import { Dispatch, SetStateAction } from "react"; +import { ActionRadio, Spacer, Typography } from "@atoms"; import { useCreateGovernanceActionForm, useScreenDimension, useTranslation, -} from '@hooks'; -import { GovernanceActionType } from '@/types/governanceAction'; +} from "@hooks"; +import { GovernanceActionType } from "@/types/governanceAction"; -import { BgCard } from '../BgCard'; +import { BgCard } from "../BgCard"; type ChooseGovernanceActionTypeProps = { setStep: Dispatch>; @@ -20,7 +20,7 @@ export const ChooseGovernanceActionType = ({ const { isMobile } = useScreenDimension(); const { getValues, setValue, watch } = useCreateGovernanceActionForm(); - const isContinueButtonDisabled = !watch('governance_action_type'); + const isContinueButtonDisabled = !watch("governance_action_type"); const onClickContinue = () => { setStep(3); @@ -30,11 +30,15 @@ export const ChooseGovernanceActionType = ({ setStep(1); }; + const onChangeType = (value: string) => { + setValue("governance_action_type", value as GovernanceActionType); + }; + // TODO: Add tooltips when they will be available const renderGovernanceActionTypes = () => Object.keys(GovernanceActionType).map( (type, index, governanceActionTypes) => { - const isChecked = getValues('governance_action_type') === type; + const isChecked = getValues("governance_action_type") === type; return (

{ - setValue('governance_action_type', value as GovernanceActionType); - }; - return ( - - {t('createGovernanceAction.chooseGATypeTitle')} + + {t("createGovernanceAction.chooseGATypeTitle")} {renderGovernanceActionTypes()} diff --git a/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/CreateGovernanceActionForm.tsx b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/CreateGovernanceActionForm.tsx index 65c868313..d9c74417c 100644 --- a/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/CreateGovernanceActionForm.tsx +++ b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/CreateGovernanceActionForm.tsx @@ -1,20 +1,17 @@ -import { Dispatch, SetStateAction, useCallback } from 'react'; -import { useFieldArray } from 'react-hook-form'; -import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; - -import { - Button, InfoText, Spacer, Typography -} from '@atoms'; -import { GOVERNANCE_ACTION_FIELDS } from '@consts'; -import { useCreateGovernanceActionForm, useTranslation } from '@hooks'; -import { Field } from '@molecules'; -import { URL_REGEX } from '@/utils'; -import { GovernanceActionField } from '@/types/governanceAction'; - -import { BgCard } from '../BgCard'; -import { ControlledField } from '../ControlledField'; - -const LINK_PLACEHOLDER = 'https://website.com/'; +import { Dispatch, SetStateAction, useCallback } from "react"; +import { useFieldArray } from "react-hook-form"; +import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline"; + +import { Button, InfoText, Spacer, Typography } from "@atoms"; +import { GOVERNANCE_ACTION_FIELDS, Placeholders } from "@consts"; +import { useCreateGovernanceActionForm, useTranslation } from "@hooks"; +import { Field } from "@molecules"; +import { URL_REGEX } from "@/utils"; +import { GovernanceActionField } from "@/types/governanceAction"; + +import { BgCard } from "../BgCard"; +import { ControlledField } from "../ControlledField"; + const MAX_NUMBER_OF_LINKS = 8; type CreateGovernanceActionFormProps = { @@ -25,27 +22,26 @@ export const CreateGovernanceActionForm = ({ setStep, }: CreateGovernanceActionFormProps) => { const { t } = useTranslation(); - const { - control, errors, getValues, register, reset, watch - } = + const { control, errors, getValues, register, reset, watch } = useCreateGovernanceActionForm(); const isError = Object.keys(errors).length > 0; - const type = getValues('governance_action_type'); + const type = getValues("governance_action_type"); const { append, fields: links, remove, } = useFieldArray({ control, - name: 'links', + name: "links", }); - // TODO: Replace any const isContinueButtonDisabled = + // TODO: Provide better typing for GOVERNANCE_ACTION_FIELDS + // to get rid of explicit type assertion Object.keys(GOVERNANCE_ACTION_FIELDS[type!]).some( - (field: any) => !watch(field), + (field) => !watch(field as unknown as Parameters[0]), ) || isError; const onClickContinue = () => { @@ -83,16 +79,9 @@ export const CreateGovernanceActionForm = ({ } }); - const addLink = useCallback(() => { - append({ link: '' }); - }, [append]); + const addLink = useCallback(() => append({ link: "" }), [append]); - const removeLink = useCallback( - (index: number) => { - remove(index); - }, - [remove], - ); + const removeLink = useCallback((index: number) => remove(index), [remove]); const renderLinks = useCallback( () => @@ -104,20 +93,20 @@ export const CreateGovernanceActionForm = ({ links.length > 1 ? ( removeLink(index)} /> ) : null } key={field.id} - label={`${t('forms.link')} ${index + 1}`} + label={`${t("forms.link")} ${index + 1}`} layoutStyles={{ mb: 3 }} - placeholder={LINK_PLACEHOLDER} + placeholder={Placeholders.LINK} name={`links.${index}.link`} rules={{ pattern: { value: URL_REGEX, - message: t('createGovernanceAction.fields.validations.url'), + message: t("createGovernanceAction.fields.validations.url"), }, }} /> @@ -127,33 +116,33 @@ export const CreateGovernanceActionForm = ({ return ( - - - {t('createGovernanceAction.formTitle')} + + + {t("createGovernanceAction.formTitle")} {renderGovernanceActionField()} - - - {t('createGovernanceAction.references')} + + + {t("createGovernanceAction.references")} {renderLinks()} {links?.length < MAX_NUMBER_OF_LINKS ? ( ) : null} diff --git a/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/ReviewCreatedGovernanceAction.tsx b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/ReviewCreatedGovernanceAction.tsx index 1a083f57c..488afddd9 100644 --- a/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/ReviewCreatedGovernanceAction.tsx +++ b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/ReviewCreatedGovernanceAction.tsx @@ -1,18 +1,18 @@ -import { Box } from '@mui/material'; -import DriveFileRenameOutlineOutlinedIcon from '@mui/icons-material/DriveFileRenameOutlineOutlined'; +import { Box } from "@mui/material"; +import DriveFileRenameOutlineOutlinedIcon from "@mui/icons-material/DriveFileRenameOutlineOutlined"; -import { Button, Spacer, Typography } from '@atoms'; -import { ICONS } from '@consts'; +import { Button, Spacer, Typography } from "@atoms"; +import { ICONS } from "@consts"; import { defaulCreateGovernanceActionValues, useCreateGovernanceActionForm, useTranslation, -} from '@hooks'; -import { LinkWithIcon } from '@molecules'; -import { openInNewTab } from '@utils'; +} from "@hooks"; +import { LinkWithIcon } from "@molecules"; +import { openInNewTab } from "@utils"; -import { Dispatch, SetStateAction } from 'react'; -import { BgCard } from '../BgCard'; +import { Dispatch, SetStateAction } from "react"; +import { BgCard } from "../BgCard"; type ReviewCreatedGovernanceActionProps = { setStep: Dispatch>; @@ -46,19 +46,19 @@ export const ReviewCreatedGovernanceAction = ({ .filter( ([key]) => !Object.keys(defaulCreateGovernanceActionValues).includes(key) || - key === 'governance_action_type', + key === "governance_action_type", ) .map(([key, value]) => { const label = - key.charAt(0).toUpperCase() + key.slice(1).replace(/_/g, ' '); + key.charAt(0).toUpperCase() + key.slice(1).replace(/_/g, " "); return ( - + {label} {value as string} @@ -79,43 +79,45 @@ export const ReviewCreatedGovernanceAction = ({ sx={{ mb: 0.5 }} variant="body2" > - {t('createGovernanceAction.supportingLinks')} + {t("createGovernanceAction.supportingLinks")} - {links.map((link: string) => - (link ? ( - } - label={link} - onClick={() => onClickLink(link)} - sx={{ mb: 1.75 }} - /> - ) : null),)} + {links.map( + (link: string) => + link && ( + } + label={link} + onClick={() => onClickLink(link)} + sx={{ mb: 1.75 }} + /> + ), + )} ) : null; }; return ( - - {t('createGovernanceAction.reviewSubmission')} + + {t("createGovernanceAction.reviewSubmission")} {renderReviewFields()} diff --git a/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StorageInformation.tsx b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StorageInformation.tsx index 51f9c1bf7..55cd6d46f 100644 --- a/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StorageInformation.tsx +++ b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StorageInformation.tsx @@ -1,10 +1,14 @@ -import { Dispatch, SetStateAction, useCallback } from "react"; +import { Dispatch, SetStateAction, useEffect } from "react"; import { Box } from "@mui/material"; import OpenInNewIcon from "@mui/icons-material/OpenInNew"; import { Button, Spacer, Typography } from "@atoms"; import { ICONS } from "@consts"; -import { useCreateGovernanceActionForm, useTranslation } from "@hooks"; +import { + useCreateGovernanceActionForm, + useTranslation, + useScreenDimension, +} from "@hooks"; import { Step } from "@molecules"; import { BgCard, ControlledField } from "@organisms"; import { URL_REGEX, openInNewTab } from "@utils"; @@ -21,22 +25,25 @@ export const StorageInformation = ({ setStep }: StorageInformationProps) => { createGovernanceAction, getValues, watch, + generateMetadata, onClickDownloadJson, isLoading, } = useCreateGovernanceActionForm(setStep); + const { screenWidth } = useScreenDimension(); - // TODO: change on correct file name const fileName = getValues("governance_action_type"); // TODO: Change link to correct - const openGuideAboutStoringInformation = useCallback( - () => openInNewTab("https://sancho.network/"), - [], - ); + const openGuideAboutStoringInformation = () => + openInNewTab("https://sancho.network/"); const isActionButtonDisabled = !watch("storingURL"); - const onClickBack = useCallback(() => setStep(5), []); + const onClickBack = () => setStep(5); + + useEffect(() => { + generateMetadata(); + }, []); return ( { {t("createGovernanceAction.storingInformationTitle")} - + } + onClick={openGuideAboutStoringInformation} + size="extraLarge" + sx={{ alignSelf: "center", width: "fit-content" }} + variant="text" > + {t("createGovernanceAction.storingInformationStep2Link")} + + {t("createGovernanceAction.storingInformationDescription")} } - sx={{ width: "fit-content" }} + startIcon={download} + sx={{ + width: "fit-content", + ml: screenWidth < 1024 ? 0 : 1.75, + mt: screenWidth < 1024 ? 1.5 : 0, + }} + variant="outlined" > {`${fileName}.jsonld`} - )} + } + componentsLayoutStyles={{ + alignItems: screenWidth < 1024 ? undefined : "center", + flexDirection: screenWidth < 1024 ? "column" : "row", + }} label={t("createGovernanceAction.storingInformationStep1Label")} stepNumber={1} /> { width: 17, }} /> - )} + } onClick={openGuideAboutStoringInformation} size="extraLarge" sx={{ width: "fit-content" }} @@ -92,16 +121,17 @@ export const StorageInformation = ({ setStep }: StorageInformationProps) => { > {t("createGovernanceAction.storingInformationStep2Link")} - )} + } label={t("createGovernanceAction.storingInformationStep2Label")} stepNumber={2} /> { }, }} /> - )} + } label={t("createGovernanceAction.storingInformationStep3Label")} stepNumber={3} /> diff --git a/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StoreDataInfo.tsx b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StoreDataInfo.tsx index a36888444..1e08357d2 100644 --- a/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StoreDataInfo.tsx +++ b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StoreDataInfo.tsx @@ -20,19 +20,13 @@ export const StoreDataInfo = ({ setStep }: StoreDataInfoProps) => { const { isMobile } = useScreenDimension(); // TODO: change link when available - const openLink = () => { - openInNewTab("https://docs.sanchogov.tools"); - }; + const openLink = () => openInNewTab("https://docs.sanchogov.tools"); const isContinueDisabled = !watch("storeData"); - const onClickContinue = () => { - setStep(6); - }; + const onClickContinue = () => setStep(6); - const onClickBack = () => { - setStep(4); - }; + const onClickBack = () => setStep(4); return ( { const { buildDRepRetirementCert, buildSignSubmitConwayCertTx, - delegateTo, - delegateTransaction, dRepID, dRepIDBech32, - govActionTransaction, - isDrepLoading, isPendingTransaction, - registerTransaction, - soleVoterTransaction, + pendingTransaction, stakeKey, - voter, } = useCardano(); const navigate = useNavigate(); - const { currentDelegation, isCurrentDelegationLoading } = useGetAdaHolderCurrentDelegationQuery(stakeKey); + const { currentDelegation } = useGetAdaHolderCurrentDelegationQuery(stakeKey); const { screenWidth } = useScreenDimension(); const { openModal } = useModal(); - const [isRetirementLoading, setIsRetirementLoading] = useState(false); - const { votingPower, powerIsLoading } = useGetAdaHolderVotingPowerQuery(stakeKey); + const [isRetirementLoading, setIsRetirementLoading] = + useState(false); + const { votingPower } = useGetAdaHolderVotingPowerQuery(stakeKey); const { t } = useTranslation(); + const { voter } = useGetVoterInfo(); const retireAsDrep = useCallback(async () => { try { setIsRetirementLoading(true); const isPendingTx = isPendingTransaction(); + if (isPendingTx) return; - const certBuilder = await buildDRepRetirementCert(); + if (!voter?.deposit) throw new Error("Can not get deposit"); + + const certBuilder = await buildDRepRetirementCert( + voter.deposit.toString(), + ); const result = await buildSignSubmitConwayCertTx({ certBuilder, - type: "registration", - registrationType: "retirement", + type: "retireAsDrep", + voterDeposit: voter.deposit.toString(), }); if (result) { openModal({ @@ -56,12 +58,13 @@ export const DashboardCards = () => { status: "success", title: t("modals.retirement.title"), message: t("modals.retirement.message"), - link: "https://adanordic.com/latest_transactions", + link: `https://adanordic.com/latest_transactions`, buttonText: t("modals.common.goToDashboard"), dataTestId: "retirement-transaction-submitted-modal", }, }); } + // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { const errorMessage = error.info ? error.info : error; @@ -83,6 +86,7 @@ export const DashboardCards = () => { buildSignSubmitConwayCertTx, isPendingTransaction, openModal, + voter?.deposit, ]); const delegationDescription = useMemo(() => { @@ -94,21 +98,24 @@ export const DashboardCards = () => { values={{ ada: correctAdaRepresentation }} /> ); - } if (currentDelegation === "drep_always_no_confidence") { + } + if (currentDelegation === "drep_always_no_confidence") { return ( ); - } if (currentDelegation === "drep_always_abstain") { + } + if (currentDelegation === "drep_always_abstain") { return ( ); - } if (currentDelegation) { + } + if (currentDelegation) { return ( { const delegationStatusTestForId = useMemo(() => { if (currentDelegation === dRepID) { return "myself"; - } if (currentDelegation === "drep_always_no_confidence") { + } + if (currentDelegation === "drep_always_no_confidence") { return "no-confidence"; - } if (currentDelegation === "drep_always_abstain") { + } + if (currentDelegation === "drep_always_abstain") { return "abstain"; - } if (currentDelegation) { + } + if (currentDelegation) { return "dRep"; } return "not_delegated"; @@ -139,7 +149,10 @@ export const DashboardCards = () => { const progressDescription = useMemo(() => { const correctAdaRepresentation = correctAdaFormat(votingPower); - if (delegateTo === dRepID) { + if (!pendingTransaction.delegate) return; + const { resourceId } = pendingTransaction.delegate; + + if (resourceId === dRepID) { return ( { /> ); } - if (delegateTo === "no confidence") { + if (resourceId === "no confidence") { return ( { /> ); } - if (delegateTo === "abstain") { + if (resourceId === "abstain") { return ( { /> ); } - if (delegateTo) { + if (resourceId) { return ( { /> ); } - }, [delegateTo, dRepID, votingPower]); + }, [pendingTransaction, dRepID, votingPower]); const navigateTo = useCallback( (path: string) => { @@ -183,12 +196,12 @@ export const DashboardCards = () => { ); const onClickGovernanceActionCardActionButton = useCallback(() => { - if (govActionTransaction.transactionHash) { + if (pendingTransaction.createGovAction) { navigate(PATHS.dashboardGovernanceActions); return; } navigate(PATHS.createGovernanceAction); - }, [govActionTransaction.transactionHash, navigate]); + }, [pendingTransaction.createGovAction, navigate]); const displayedDelegationId = useMemo(() => { const restrictedNames = [ @@ -198,7 +211,8 @@ export const DashboardCards = () => { "abstain", "no confidence", ]; - if (delegateTransaction?.transactionHash) { + if (pendingTransaction.delegate) { + const delegateTo = pendingTransaction.delegate.resourceId; if (!restrictedNames.includes(delegateTo)) { return delegateTo.includes("drep") ? delegateTo @@ -210,105 +224,92 @@ export const DashboardCards = () => { return formHexToBech32(currentDelegation); } return undefined; - }, [ - currentDelegation, - dRepID, - delegateTo, - delegateTransaction, - formHexToBech32, - ]); + }, [currentDelegation, dRepID, pendingTransaction, formHexToBech32]); const registrationCardDescription = useMemo(() => { - if (registerTransaction.transactionHash) { - switch (registerTransaction.type) { - case "retirement": - return t("dashboard.registration.retirementInProgress"); - case "registration": - return t("dashboard.registration.registrationInProgress"); - default: - return t("dashboard.registration.metadataUpdateInProgress"); - } - } else if (voter?.isRegisteredAsDRep || voter?.wasRegisteredAsDRep) { + if (pendingTransaction.registerAsDrep) + return t("dashboard.registration.registrationInProgress"); + + if (pendingTransaction.retireAsDrep) + return t("dashboard.registration.retirementInProgress"); + + if (pendingTransaction.updateMetaData) + return t("dashboard.registration.metadataUpdateInProgress"); + + if (voter?.isRegisteredAsDRep || voter?.wasRegisteredAsDRep) return t("dashboard.registration.holdersCanDelegate"); - } else { - return t("dashboard.registration.ifYouWant"); - } + + return t("dashboard.registration.ifYouWant"); }, [ - registerTransaction.transactionHash, - registerTransaction.type, + pendingTransaction, voter?.isRegisteredAsDRep, voter?.wasRegisteredAsDRep, ]); const soleVoterCardDescription = useMemo(() => { - if (soleVoterTransaction.transactionHash) { - switch (soleVoterTransaction.type) { - case "retirement": - return "dashboard.soleVoter.retirementInProgress"; - default: - return "dashboard.soleVoter.registrationInProgress"; - } - } else if (voter?.isRegisteredAsSoleVoter) { + if (pendingTransaction.registerAsSoleVoter) + return "dashboard.soleVoter.registrationInProgress"; + + if (pendingTransaction.retireAsSoleVoter) + return "dashboard.soleVoter.retirementInProgress"; + + if (voter?.isRegisteredAsSoleVoter) return "dashboard.soleVoter.isRegisteredDescription"; - } else if (voter?.wasRegisteredAsSoleVoter) { + + if (voter?.wasRegisteredAsSoleVoter) return "dashboard.soleVoter.wasRegisteredDescription"; - } else { - return "dashboard.soleVoter.registerDescription"; - } + + return "dashboard.soleVoter.registerDescription"; }, [ - soleVoterTransaction.transactionHash, - soleVoterTransaction.type, + pendingTransaction, voter?.isRegisteredAsSoleVoter, voter?.wasRegisteredAsSoleVoter, ]); const registrationCardTitle = useMemo(() => { - if (registerTransaction?.transactionHash) { - switch (registerTransaction.type) { - case "retirement": - return t("dashboard.registration.dRepRetirement"); - case "registration": - return t("dashboard.registration.dRepRegistration"); - default: - return t("dashboard.registration.dRepUpdate"); - } - } else if (voter?.isRegisteredAsDRep) { + if (pendingTransaction.retireAsDrep) + return t("dashboard.registration.dRepRetirement"); + + if (pendingTransaction.registerAsDrep) + return t("dashboard.registration.dRepRegistration"); + + if (pendingTransaction.updateMetaData) + return t("dashboard.registration.dRepUpdate"); + + if (voter?.isRegisteredAsDRep) return t("dashboard.registration.youAreRegistered"); - } else if (voter?.wasRegisteredAsDRep) { + + if (voter?.wasRegisteredAsDRep) return t("dashboard.registration.registerAgain"); - } else { - return t("dashboard.registration.registerAsDRep"); - } + + return t("dashboard.registration.registerAsDRep"); }, [ - registerTransaction?.transactionHash, - registerTransaction.type, + pendingTransaction, voter?.isRegisteredAsDRep, voter?.wasRegisteredAsDRep, ]); const soleVoterCardTitle = useMemo(() => { - if (soleVoterTransaction?.transactionHash) { - switch (soleVoterTransaction.type) { - case "retirement": - return t("dashboard.soleVoter.retirement"); - default: - return t("dashboard.soleVoter.registration"); - } - } else if (voter?.isRegisteredAsSoleVoter) { + if (pendingTransaction.retireAsSoleVoter) + return t("dashboard.soleVoter.retirement"); + + if (pendingTransaction.registerAsSoleVoter) + return t("dashboard.soleVoter.registration"); + + if (voter?.isRegisteredAsSoleVoter) return t("dashboard.soleVoter.youAreSoleVoterTitle"); - } else if (voter?.wasRegisteredAsSoleVoter) { + + if (voter?.wasRegisteredAsSoleVoter) return t("dashboard.soleVoter.wasSoleVoterTitle"); - } else { - return t("dashboard.soleVoter.registerTitle"); - } + + return t("dashboard.soleVoter.registerTitle"); }, [ - soleVoterTransaction?.transactionHash, - soleVoterTransaction.type, + pendingTransaction, voter?.isRegisteredAsSoleVoter, voter?.isRegisteredAsSoleVoter, ]); - return isDrepLoading ? ( + return !voter || !votingPower ? ( { screenWidth < 1280 ? "repeat(1, minmax(300px, 530px))" : screenWidth >= 1728 - ? "repeat(3, minmax(300px, 570px))" - : "repeat(2, minmax(300px, 530px))", + ? "repeat(3, minmax(300px, 570px))" + : "repeat(2, minmax(300px, 530px))", justifyContent: screenWidth < 1024 ? "center" : "flex-start", px: screenWidth < 640 ? 2 : 5, py: 3, @@ -344,46 +345,46 @@ export const DashboardCards = () => { } dataTestidSecondButton="delegate-learn-more-button" dataTestidDrepIdBox="delegated-to-drep-id" - isLoading={isCurrentDelegationLoading || powerIsLoading} description={ - delegateTransaction?.transactionHash + pendingTransaction.delegate ? progressDescription : delegationDescription } dataTestidDelegationStatus={ - delegateTransaction?.transactionHash + pendingTransaction.delegate ? "voting-power-delegation-status-in-progress" : `voting-power-delegation-status-${delegationStatusTestForId}` } firstButtonAction={() => navigateTo(PATHS.delegateTodRep)} firstButtonLabel={ - delegateTransaction?.transactionHash + pendingTransaction.delegate ? "" : currentDelegation - ? t("dashboard.delegation.changeDelegation") - : t("delegate") + ? t("dashboard.delegation.changeDelegation") + : t("delegate") } firstButtonVariant={currentDelegation ? "outlined" : "contained"} imageURL={IMAGES.govActionDelegateImage} cardId={displayedDelegationId} - inProgress={!!delegateTransaction?.transactionHash} + inProgress={!!pendingTransaction.delegate} cardTitle={t("dashboard.delegation.dRepDelegatedTo")} secondButtonAction={ - delegateTransaction?.transactionHash + pendingTransaction.delegate ? () => openInNewTab("https://adanordic.com/latest_transactions") - : () => openInNewTab( - "https://docs.sanchogov.tools/faqs/ways-to-use-your-voting-power", - ) + : () => + openInNewTab( + "https://docs.sanchogov.tools/faqs/ways-to-use-your-voting-power", + ) } secondButtonLabel={ - delegateTransaction?.transactionHash + pendingTransaction.delegate ? t("seeTransaction") : currentDelegation - ? "" - : t("learnMore") + ? "" + : t("learnMore") } title={ - delegateTransaction?.transactionHash ? ( + pendingTransaction.delegate ? ( t("dashboard.delegation.votingPowerDelegation") ) : currentDelegation ? ( @@ -403,11 +404,11 @@ export const DashboardCards = () => { voter?.isRegisteredAsDRep ? "outlined" : "contained" } secondButtonVariant={ - registerTransaction?.transactionHash + pendingTransaction.registerAsDrep || pendingTransaction.retireAsDrep ? "outlined" : voter?.isRegisteredAsDRep - ? "text" - : "outlined" + ? "text" + : "outlined" } dataTestidSecondButton={ voter?.isRegisteredAsDRep @@ -422,33 +423,40 @@ export const DashboardCards = () => { } firstButtonIsLoading={isRetirementLoading} firstButtonLabel={ - registerTransaction?.transactionHash + pendingTransaction.registerAsDrep || pendingTransaction.retireAsDrep ? "" : t( - `dashboard.registration.${ - voter?.isRegisteredAsDRep ? "retire" : "register" - }`, - ) + `dashboard.registration.${ + voter?.isRegisteredAsDRep ? "retire" : "register" + }`, + ) + } + inProgress={ + !!( + pendingTransaction.registerAsDrep || + pendingTransaction.retireAsDrep || + pendingTransaction.updateMetaData + ) } - inProgress={!!registerTransaction?.transactionHash} imageURL={IMAGES.govActionRegisterImage} secondButtonAction={ - registerTransaction?.transactionHash + pendingTransaction.registerAsDrep || pendingTransaction.retireAsDrep ? () => openInNewTab("https://adanordic.com/latest_transactions") : voter?.isRegisteredAsDRep - ? () => { + ? () => { navigateTo(PATHS.updateMetadata); } - : () => openInNewTab( - "https://docs.sanchogov.tools/faqs/what-does-it-mean-to-register-as-a-drep", - ) + : () => + openInNewTab( + "https://docs.sanchogov.tools/faqs/what-does-it-mean-to-register-as-a-drep", + ) } secondButtonLabel={ - registerTransaction?.transactionHash + pendingTransaction.registerAsDrep || pendingTransaction.retireAsDrep ? t("seeTransaction") : voter?.isRegisteredAsDRep - ? t("dashboard.registration.changeMetadata") - : t("learnMore") + ? t("dashboard.registration.changeMetadata") + : t("learnMore") } cardId={ voter?.isRegisteredAsDRep || voter?.wasRegisteredAsDRep @@ -466,42 +474,49 @@ export const DashboardCards = () => { {/* SOLE VOTER CARD */} - )} + } firstButtonLabel={ - soleVoterTransaction?.transactionHash + pendingTransaction.registerAsSoleVoter ? "" : t( - voter?.isRegisteredAsSoleVoter - ? "dashboard.soleVoter.retire" - : voter?.wasRegisteredAsSoleVoter + voter?.isRegisteredAsSoleVoter + ? "dashboard.soleVoter.retire" + : voter?.wasRegisteredAsSoleVoter ? "dashboard.soleVoter.reRegister" : "dashboard.soleVoter.register", - ) + ) + } + firstButtonAction={() => + navigateTo( + voter?.isRegisteredAsSoleVoter + ? PATHS.retireAsSoleVoter + : PATHS.registerAsSoleVoter, + ) } - firstButtonAction={() => navigateTo( - voter?.isRegisteredAsSoleVoter - ? PATHS.retireAsSoleVoter - : PATHS.registerAsSoleVoter, - )} firstButtonVariant={ voter?.isRegisteredAsSoleVoter ? "outlined" : "contained" } secondButtonLabel={t("learnMore")} - secondButtonAction={() => openInNewTab( - "https://docs.sanchogov.tools/faqs/what-does-it-mean-to-register-as-a-drep", - )} + secondButtonAction={() => + openInNewTab( + "https://docs.sanchogov.tools/faqs/what-does-it-mean-to-register-as-a-drep", + ) + } secondButtonVariant="outlined" imageURL={IMAGES.soleVoterImage} /> @@ -527,14 +542,16 @@ export const DashboardCards = () => { firstButtonAction={onClickGovernanceActionCardActionButton} firstButtonLabel={t( `dashboard.proposeGovernanceAction.${ - govActionTransaction.transactionHash ? "view" : "propose" + pendingTransaction.createGovAction ? "view" : "propose" }`, )} - inProgress={!!govActionTransaction.transactionHash} + inProgress={!!pendingTransaction.createGovAction} secondButtonLabel={t("learnMore")} - secondButtonAction={() => openInNewTab( - "https://docs.sanchogov.tools/faqs/what-is-a-governance-action", - )} + secondButtonAction={() => + openInNewTab( + "https://docs.sanchogov.tools/faqs/what-is-a-governance-action", + ) + } secondButtonVariant="outlined" imageURL={IMAGES.proposeGovActionImage} title={t("dashboard.proposeGovernanceAction.title")} diff --git a/govtool/frontend/src/components/organisms/DashboardDrawerMobile.tsx b/govtool/frontend/src/components/organisms/DashboardDrawerMobile.tsx index ff5e675a1..ee75b1df1 100644 --- a/govtool/frontend/src/components/organisms/DashboardDrawerMobile.tsx +++ b/govtool/frontend/src/components/organisms/DashboardDrawerMobile.tsx @@ -1,12 +1,9 @@ -import { - Box, Grid, IconButton, SwipeableDrawer, -} from "@mui/material"; +import { Box, Grid, IconButton, SwipeableDrawer } from "@mui/material"; import { Background, Link } from "@atoms"; import { CONNECTED_NAV_ITEMS, ICONS } from "@consts"; -import { useCardano } from "@context"; import { DRepInfoCard, WalletInfoCard } from "@molecules"; -import { useScreenDimension } from "@hooks"; +import { useGetVoterInfo, useScreenDimension } from "@hooks"; import { openInNewTab } from "@utils"; import { DashboardDrawerMobileProps } from "./types"; @@ -20,7 +17,7 @@ export const DashboardDrawerMobile = ({ setIsDrawerOpen, }: DashboardDrawerMobileProps) => { const { screenWidth } = useScreenDimension(); - const { voter } = useCardano(); + const { voter } = useGetVoterInfo(); const openDrawer = () => { setIsDrawerOpen(true); @@ -58,13 +55,13 @@ export const DashboardDrawerMobile = ({ width: screenWidth - CALCULATED_DRAWER_PADDING, }} > - + app-logo - + drawer @@ -74,6 +71,8 @@ export const DashboardDrawerMobile = ({ {...navItem} size="big" onClick={() => { + // TODO: Refine if it is needed to remove this eslint-disable + // eslint-disable-next-line no-unused-expressions navItem.newTabLink && openInNewTab(navItem.newTabLink); setIsDrawerOpen(false); }} diff --git a/govtool/frontend/src/components/organisms/DashboardGovernanceActionDetails.tsx b/govtool/frontend/src/components/organisms/DashboardGovernanceActionDetails.tsx index 8a55bbd5c..586005b07 100644 --- a/govtool/frontend/src/components/organisms/DashboardGovernanceActionDetails.tsx +++ b/govtool/frontend/src/components/organisms/DashboardGovernanceActionDetails.tsx @@ -14,9 +14,9 @@ import { } from "@mui/material"; import { ICONS, PATHS } from "@consts"; -import { useCardano } from "@context"; import { useGetProposalQuery, + useGetVoterInfo, useScreenDimension, useTranslation, } from "@hooks"; @@ -28,7 +28,7 @@ import { } from "@utils"; export const DashboardGovernanceActionDetails = () => { - const { voter } = useCardano(); + const { voter } = useGetVoterInfo(); const { state, hash } = useLocation(); const navigate = useNavigate(); const { isMobile, screenWidth } = useScreenDimension(); @@ -84,18 +84,20 @@ export const DashboardGovernanceActionDetails = () => { display: "flex", textDecoration: "none", }} - onClick={() => navigate( - state && state.openedFromCategoryPage - ? generatePath(PATHS.dashboardGovernanceActionsCategory, { - category: state.type, - }) - : PATHS.dashboardGovernanceActions, - { - state: { - isVotedListOnLoad: !!(state && state.vote), + onClick={() => + navigate( + state && state.openedFromCategoryPage + ? generatePath(PATHS.dashboardGovernanceActionsCategory, { + category: state.type, + }) + : PATHS.dashboardGovernanceActions, + { + state: { + isVotedListOnLoad: !!(state && state.vote), + }, }, - }, - )} + ) + } > { {t("govActions.withIdNotExist.partOne")} -  +   - + {` ${shortenedGovActionId} `} diff --git a/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx b/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx index 280147602..d865e5e8d 100644 --- a/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx +++ b/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx @@ -1,16 +1,20 @@ import { useState, useCallback, useEffect } from "react"; -import { - Box, CircularProgress, Tab, Tabs, styled, -} from "@mui/material"; +import { Box, CircularProgress, Tab, Tabs, styled } from "@mui/material"; import { useLocation } from "react-router-dom"; -import { useCardano } from "@context"; -import { useScreenDimension, useTranslation } from "@hooks"; -import { DataActionsBar } from "@molecules"; +import { GOVERNANCE_ACTIONS_FILTERS } from '@consts'; +import { useCardano } from '@context'; +import { + useGetProposalsQuery, + useGetVoterInfo, + useScreenDimension, + useTranslation, +} from '@hooks'; +import { DataActionsBar } from '@molecules'; import { GovernanceActionsToVote, DashboardGovernanceActionsVotedOn, -} from "@organisms"; +} from '@organisms'; interface TabPanelProps { children?: React.ReactNode; @@ -18,6 +22,10 @@ interface TabPanelProps { value: number; } +const defaultCategories = GOVERNANCE_ACTIONS_FILTERS.map( + (category) => category.key, +); + const CustomTabPanel = (props: TabPanelProps) => { const { children, value, index } = props; @@ -28,8 +36,8 @@ const CustomTabPanel = (props: TabPanelProps) => { id={`simple-tabpanel-${index}`} aria-labelledby={`simple-tab-${index}`} style={{ - display: "flex", - flexDirection: "column", + display: 'flex', + flexDirection: 'column', flex: value !== index ? 0 : 1, }} > @@ -45,32 +53,41 @@ type StyledTabProps = { const StyledTab = styled((props: StyledTabProps) => ( ))(() => ({ - textTransform: "none", + textTransform: 'none', fontWeight: 400, fontSize: 16, - color: "#242232", - "&.Mui-selected": { - color: "#FF640A", + color: '#242232', + '&.Mui-selected': { + color: '#FF640A', fontWeight: 500, }, })); export const DashboardGovernanceActions = () => { - const [searchText, setSearchText] = useState(""); + const [searchText, setSearchText] = useState(''); const [filtersOpen, setFiltersOpen] = useState(false); const [chosenFilters, setChosenFilters] = useState([]); const [sortOpen, setSortOpen] = useState(false); - const [chosenSorting, setChosenSorting] = useState(""); + const [chosenSorting, setChosenSorting] = useState(''); + const { voter } = useGetVoterInfo(); + const { isMobile } = useScreenDimension(); + const { t } = useTranslation(); + const { isEnableLoading } = useCardano(); + + const queryFilters = + chosenFilters.length > 0 ? chosenFilters : defaultCategories; + + const { proposals, isProposalsLoading } = useGetProposalsQuery({ + filters: queryFilters, + sorting: chosenSorting, + searchPhrase: searchText, + }); const { state } = useLocation(); const [content, setContent] = useState( state && state.isVotedListOnLoad ? 1 : 0, ); - const { voter, isDrepLoading } = useCardano(); - const { isMobile } = useScreenDimension(); - const { t } = useTranslation(); - const handleChange = (_event: React.SyntheticEvent, newValue: number) => { setContent(newValue); }; @@ -95,83 +112,86 @@ export const DashboardGovernanceActions = () => { display="flex" flexDirection="column" > - {isDrepLoading ? ( - - - - ) : ( - <> - - {(voter?.isRegisteredAsDRep || voter?.isRegisteredAsSoleVoter) && ( - - + + {!proposals || !voter || isEnableLoading || isProposalsLoading ? ( + + + + ) : ( + <> + {(voter?.isRegisteredAsDRep || voter?.isRegisteredAsSoleVoter) && ( + + + + + )} + + + - + + - - )} - - - - - - - - - )} + + + )} + ); }; diff --git a/govtool/frontend/src/components/organisms/DashboardGovernanceActionsVotedOn.tsx b/govtool/frontend/src/components/organisms/DashboardGovernanceActionsVotedOn.tsx index c743df44c..e2b024610 100644 --- a/govtool/frontend/src/components/organisms/DashboardGovernanceActionsVotedOn.tsx +++ b/govtool/frontend/src/components/organisms/DashboardGovernanceActionsVotedOn.tsx @@ -1,16 +1,16 @@ -import { useMemo } from "react"; -import { Box, Typography, CircularProgress } from "@mui/material"; +import { useMemo } from 'react'; +import { Box, Typography, CircularProgress } from '@mui/material'; -import { GovernanceVotedOnCard } from "@molecules"; +import { GovernanceVotedOnCard } from '@molecules'; import { useGetDRepVotesQuery, useScreenDimension, useTranslation, -} from "@hooks"; -import { Slider } from "."; -import { getProposalTypeLabel } from "@/utils/getProposalTypeLabel"; -import { getFullGovActionId } from "@/utils"; -import { useCardano } from "@/context"; +} from '@hooks'; +import { Slider } from '.'; +import { getProposalTypeLabel } from '@/utils/getProposalTypeLabel'; +import { getFullGovActionId } from '@/utils'; +import { useCardano } from '@/context'; interface DashboardGovernanceActionsVotedOnProps { filters: string[]; @@ -25,7 +25,7 @@ export const DashboardGovernanceActionsVotedOn = ({ }: DashboardGovernanceActionsVotedOnProps) => { const { data, areDRepVotesLoading } = useGetDRepVotesQuery(filters, sorting); const { isMobile } = useScreenDimension(); - const { voteTransaction } = useCardano(); + const { pendingTransaction } = useCardano(); const { t } = useTranslation(); const filteredData = useMemo(() => { @@ -33,17 +33,16 @@ export const DashboardGovernanceActionsVotedOn = ({ return data .map((entry) => ({ ...entry, - actions: entry.actions.filter((action) => getFullGovActionId( - action.proposal.txHash, - action.proposal.index, - ) - .toLowerCase() - .includes(searchPhrase.toLowerCase())), + actions: entry.actions.filter((action) => + getFullGovActionId(action.proposal.txHash, action.proposal.index) + .toLowerCase() + .includes(searchPhrase.toLowerCase()), + ), })) .filter((entry) => entry.actions.length > 0); } return data; - }, [data, searchPhrase, voteTransaction.transactionHash]); + }, [data, searchPhrase, pendingTransaction.vote]); return areDRepVotesLoading ? ( @@ -53,11 +52,11 @@ export const DashboardGovernanceActionsVotedOn = ({ <> {!data.length ? ( - {t("govActions.youHaventVotedYet")} + {t('govActions.youHaventVotedYet')} ) : !filteredData?.length ? ( - {t("govActions.noResultsForTheSearch")} + {t('govActions.noResultsForTheSearch')} ) : ( <> @@ -69,19 +68,18 @@ export const DashboardGovernanceActionsVotedOn = ({ title={getProposalTypeLabel(item.title)} navigateKey={item.title} searchPhrase={searchPhrase} - data={item.actions.map((item) => ( + data={item.actions.map((action) => (
))} diff --git a/govtool/frontend/src/components/organisms/DashboardTopNav.tsx b/govtool/frontend/src/components/organisms/DashboardTopNav.tsx index 1c9a497b7..787735251 100644 --- a/govtool/frontend/src/components/organisms/DashboardTopNav.tsx +++ b/govtool/frontend/src/components/organisms/DashboardTopNav.tsx @@ -47,8 +47,8 @@ export const DashboardTopNav = ({ windowScroll > POSITION_TO_BLUR ? "rgba(256, 256, 256, 0.7)" : isMobile - ? "#FBFBFF59" - : "transparent", + ? "#FBFBFF59" + : "transparent", borderBottom: "1px solid #D6E2FF", display: "flex", justifyContent: "space-between", @@ -62,7 +62,12 @@ export const DashboardTopNav = ({ > {isMobile ? ( - + app-logo ) : null} {!isMobile && title ? ( {title} @@ -76,7 +81,7 @@ export const DashboardTopNav = ({ sx={{ padding: 0, marginLeft: 1 }} onClick={openDrawer} > - + drawer )} diff --git a/govtool/frontend/src/components/organisms/DelegateTodRepStepOne.tsx b/govtool/frontend/src/components/organisms/DelegateTodRepStepOne.tsx index 048a36574..6524497d4 100644 --- a/govtool/frontend/src/components/organisms/DelegateTodRepStepOne.tsx +++ b/govtool/frontend/src/components/organisms/DelegateTodRepStepOne.tsx @@ -1,17 +1,14 @@ -import { - useEffect, useState, useCallback, useMemo, -} from "react"; +import { useEffect, useState, useCallback, useMemo } from "react"; import { useNavigate } from "react-router-dom"; import { Box, Grid } from "@mui/material"; -import { - ActionRadio, Button, LoadingButton, Typography, -} from "@atoms"; +import { ActionRadio, Button, LoadingButton, Typography } from "@atoms"; import { ICONS, PATHS } from "@consts"; import { useCardano, useModal } from "@context"; import { useGetAdaHolderCurrentDelegationQuery, useGetAdaHolderVotingPowerQuery, + useGetVoterInfo, useScreenDimension, useTranslation, } from "@hooks"; @@ -25,17 +22,18 @@ interface DelegateProps { export const DelegateTodRepStepOne = ({ setStep }: DelegateProps) => { const navigate = useNavigate(); const { - voter, dRepID, buildSignSubmitConwayCertTx, buildVoteDelegationCert, stakeKey, } = useCardano(); + const { voter } = useGetVoterInfo(); const { currentDelegation } = useGetAdaHolderCurrentDelegationQuery(stakeKey); const { openModal, closeModal } = useModal(); const [areOptions, setAreOptions] = useState(false); const [chosenOption, setChosenOption] = useState(""); - const [isDelegationLoading, setIsDelegationLoading] = useState(false); + const [isDelegationLoading, setIsDelegationLoading] = + useState(false); const { palette: { boxShadow2 }, } = theme; @@ -83,8 +81,8 @@ export const DelegateTodRepStepOne = ({ setStep }: DelegateProps) => { useEffect(() => { if ( - !areOptions - && (chosenOption === "no confidence" || chosenOption === "abstain") + !areOptions && + (chosenOption === "no confidence" || chosenOption === "abstain") ) { setChosenOption(""); } @@ -96,9 +94,11 @@ export const DelegateTodRepStepOne = ({ setStep }: DelegateProps) => { const certBuilder = await buildVoteDelegationCert(chosenOption); const result = await buildSignSubmitConwayCertTx({ certBuilder, - type: "delegation", + type: "delegate", + resourceId: chosenOption, }); if (result) openSuccessDelegationModal(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { const errorMessage = error.info ? error.info : error; @@ -108,53 +108,59 @@ export const DelegateTodRepStepOne = ({ setStep }: DelegateProps) => { } }, [chosenOption, buildSignSubmitConwayCertTx, buildVoteDelegationCert]); - const renderDelegateButton = useMemo(() => ( - ( + { - if (chosenOption === "Delegate to DRep") { - setStep(2); - } else { - delegate(); - } - }} - size="extraLarge" - sx={{ - px: 6, - width: isMobile ? "100%" : "auto", - }} - variant="contained" - > - {chosenOption !== dRepID ? t("nextStep") : t("delegate")} - - ), [ - chosenOption, - delegate, - voter?.isRegisteredAsDRep, - voter?.isRegisteredAsSoleVoter, - dRepID, - isDelegationLoading, - isMobile, - ]); + disabled={!chosenOption} + isLoading={isDelegationLoading} + onClick={() => { + if (chosenOption === "Delegate to DRep") { + setStep(2); + } else { + delegate(); + } + }} + size="extraLarge" + sx={{ + px: 6, + width: isMobile ? "100%" : "auto", + }} + variant="contained" + > + {chosenOption !== dRepID ? t("nextStep") : t("delegate")} + + ), + [ + chosenOption, + delegate, + voter?.isRegisteredAsDRep, + voter?.isRegisteredAsSoleVoter, + dRepID, + isDelegationLoading, + isMobile, + ], + ); - const renderCancelButton = useMemo(() => ( - - ), [isMobile]); + const renderCancelButton = useMemo( + () => ( + + ), + [isMobile], + ); return ( { flexDirection="column" rowGap={3} > - {(voter?.isRegisteredAsDRep || voter?.isRegisteredAsSoleVoter) - && currentDelegation !== dRepID && ( + {(voter?.isRegisteredAsDRep || voter?.isRegisteredAsSoleVoter) && + currentDelegation !== dRepID && ( { dataTestId="delegate-to-myself-card" /> - )} + )} { isDelegationLoading, } = useDelegateTodRepForm(); - const renderDelegateButton = useMemo(() => ( - - {t("delegate")} - - ), [isDelegateButtonDisabled, delegate, isMobile, isDelegationLoading]); + const renderDelegateButton = useMemo( + () => ( + + {t("delegate")} + + ), + [isDelegateButtonDisabled, delegate, isMobile, isDelegationLoading], + ); - const renderBackButton = useMemo(() => ( - - ), [isMobile]); + const renderBackButton = useMemo( + () => ( + + ), + [isMobile], + ); return ( { openInNewTab( - "https://docs.sanchogov.tools/faqs/where-can-i-find-a-drep-id", - )} + onClick={() => + openInNewTab( + "https://docs.sanchogov.tools/faqs/where-can-i-find-a-drep-id", + ) + } alignSelf="center" mt={4} sx={[{ "&:hover": { cursor: "pointer" } }]} diff --git a/govtool/frontend/src/components/organisms/Drawer.tsx b/govtool/frontend/src/components/organisms/Drawer.tsx index 2695cfd88..b69c1dd51 100644 --- a/govtool/frontend/src/components/organisms/Drawer.tsx +++ b/govtool/frontend/src/components/organisms/Drawer.tsx @@ -1,40 +1,38 @@ -import { Box, Grid } from "@mui/material"; -import { NavLink } from "react-router-dom"; +import { Box, Grid } from '@mui/material'; +import { NavLink } from 'react-router-dom'; import { DrawerLink, Typography } from "@atoms"; -import { - CONNECTED_NAV_ITEMS, ICONS, IMAGES, PATHS, -} from "@consts"; -import { useCardano } from "@context"; -import { useTranslation } from "@hooks"; +import { CONNECTED_NAV_ITEMS, ICONS, IMAGES, PATHS } from "@consts"; +import { useGetVoterInfo, useTranslation } from "@hooks"; import { WalletInfoCard, DRepInfoCard } from "@molecules"; import { openInNewTab } from "@utils"; export const Drawer = () => { - const { voter } = useCardano(); + const { voter } = useGetVoterInfo(); const { t } = useTranslation(); return ( app-logo { openInNewTab( - "https://docs.sanchogov.tools/support/get-help-in-discord", - )} + onClick={() => + openInNewTab( + "https://docs.sanchogov.tools/support/get-help-in-discord", + ) + } /> - {t("footer.copyright")} + {t('footer.copyright')} diff --git a/govtool/frontend/src/components/organisms/DrawerMobile.tsx b/govtool/frontend/src/components/organisms/DrawerMobile.tsx index 6086048a3..6fd27d050 100644 --- a/govtool/frontend/src/components/organisms/DrawerMobile.tsx +++ b/govtool/frontend/src/components/organisms/DrawerMobile.tsx @@ -1,10 +1,6 @@ -import { - Box, Grid, IconButton, SwipeableDrawer, -} from "@mui/material"; +import { Box, Grid, IconButton, SwipeableDrawer } from "@mui/material"; -import { - Background, Button, Link, Typography, -} from "@atoms"; +import { Background, Button, Link, Typography } from "@atoms"; import { ICONS, IMAGES, NAV_ITEMS } from "@consts"; import { useScreenDimension, useTranslation } from "@hooks"; import { useModal } from "@context"; @@ -24,7 +20,8 @@ export const DrawerMobile = ({ const { openModal } = useModal(); const { t } = useTranslation(); - const onClickHelp = () => openInNewTab("https://docs.sanchogov.tools/support/get-help-in-discord"); + const onClickHelp = () => + openInNewTab("https://docs.sanchogov.tools/support/get-help-in-discord"); return ( - + app-logo setIsDrawerOpen(false)} > - + close-drawer
{isConnectButton ? ( @@ -96,7 +93,7 @@ export const DrawerMobile = ({ onClick={onClickHelp} sx={{ alignItems: "center", display: "flex", p: DRAWER_PADDING }} > - + help {t("menu.help")} diff --git a/govtool/frontend/src/components/organisms/Footer.tsx b/govtool/frontend/src/components/organisms/Footer.tsx index b4313c86b..a4662818c 100644 --- a/govtool/frontend/src/components/organisms/Footer.tsx +++ b/govtool/frontend/src/components/organisms/Footer.tsx @@ -24,7 +24,9 @@ export const Footer = () => { openInNewTab("https://docs.sanchogov.tools/legal/privacy-policy")} + onClick={() => + openInNewTab("https://docs.sanchogov.tools/legal/privacy-policy") + } sx={[{ textDecoration: "none" }]} mr={6} > diff --git a/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx b/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx index fa7817a75..02e0357b7 100644 --- a/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx +++ b/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx @@ -10,7 +10,7 @@ import { Button, Typography } from "../atoms"; type GovernanceActionDetailsCardProps = { abstainVotes: number; createdDate: string; - details: any; + details: unknown; expiryDate: string; noVotes: number; type: string; diff --git a/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx b/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx index ec593714f..41eaa2bb2 100644 --- a/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx +++ b/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx @@ -1,129 +1,95 @@ -import { useMemo } from "react"; +/* eslint-disable no-unsafe-optional-chaining */ import { useNavigate, generatePath } from "react-router-dom"; -import { Box, CircularProgress } from "@mui/material"; +import { Box } from "@mui/material"; -import { Typography } from "@atoms"; -import { - useGetProposalsQuery, - useScreenDimension, - useTranslation, -} from "@hooks"; -import { GovernanceActionCard } from "@molecules"; -import { GOVERNANCE_ACTIONS_FILTERS, PATHS } from "@consts"; -import { useCardano } from "@context"; -import { getProposalTypeLabel, getFullGovActionId, openInNewTab } from "@utils"; -import { Slider } from "./Slider"; +import { Typography } from '@atoms'; +import { PATHS } from '@consts'; +import { useCardano } from '@context'; +import { useScreenDimension, useTranslation } from '@hooks'; +import { GovernanceActionCard } from '@molecules'; +import { getProposalTypeLabel, getFullGovActionId, openInNewTab } from '@utils'; +import { Slider } from './Slider'; type GovernanceActionsToVoteProps = { filters: string[]; + sorting: string; + proposals: { title: string; actions: ActionType[] }[]; onDashboard?: boolean; searchPhrase?: string; - sorting: string; }; -const defaultCategories = GOVERNANCE_ACTIONS_FILTERS.map( - (category) => category.key, -); - export const GovernanceActionsToVote = ({ filters, onDashboard = true, - searchPhrase = "", + proposals, + searchPhrase, sorting, }: GovernanceActionsToVoteProps) => { - const { voteTransaction } = useCardano(); + const { pendingTransaction } = useCardano(); const navigate = useNavigate(); const { isMobile } = useScreenDimension(); const { t } = useTranslation(); - const queryFilters = filters.length > 0 ? filters : defaultCategories; - - const { proposals, isProposalsLoading } = useGetProposalsQuery({ - filters: queryFilters, - sorting, - }); - - const groupedByType = (data?: ActionType[]) => data?.reduce((groups, item) => { - const itemType = item.type; - - if (!groups[itemType]) { - groups[itemType] = { - title: itemType, - actions: [], - }; - } - - groups[itemType].actions.push(item); - - return groups; - }, {}); - - const mappedData = useMemo(() => { - const groupedData = groupedByType( - proposals?.filter((i) => getFullGovActionId(i.txHash, i.index) - .toLowerCase() - .includes(searchPhrase.toLowerCase())), - ); - return Object.values(groupedData ?? []) as ToVoteDataType; - }, [proposals, searchPhrase]); - - return !isProposalsLoading ? ( + return ( <> - {!mappedData.length ? ( + {!proposals.length ? ( - {t("govActions.noResultsForTheSearch")} + {t('govActions.noResultsForTheSearch')} ) : ( <> - {mappedData?.map((item, index) => ( + {proposals?.map((item, index) => ( ( + data={item.actions.slice(0, 6).map((action) => (
+ onDashboard && + pendingTransaction.vote?.resourceId === + action?.txHash + action?.index + ? openInNewTab( + "https://adanordic.com/latest_transactions", + ) + : navigate( onDashboard - && voteTransaction?.proposalId - === item?.txHash + item?.index - } - onClick={() => (onDashboard - && voteTransaction?.proposalId - === item?.txHash + item?.index - ? openInNewTab( - "https://adanordic.com/latest_transactions", - ) - : navigate( - onDashboard - ? generatePath( - PATHS.dashboardGovernanceActionsAction, - { - proposalId: getFullGovActionId( - item.txHash, - item.index, + ? generatePath( + PATHS.dashboardGovernanceActionsAction, + { + proposalId: getFullGovActionId( + action.txHash, + action.index, + ), + }, + ) + : PATHS.governanceActionsAction.replace( + ":proposalId", + getFullGovActionId( + action.txHash, + action.index, ), - }, - ) - : PATHS.governanceActionsAction.replace( - ":proposalId", - getFullGovActionId( - item.txHash, - item.index, ), - ), - { - state: { ...item }, - }, - ))} + { + state: { ...action }, + }, + ) + } />
))} @@ -136,23 +102,13 @@ export const GovernanceActionsToVote = ({ sorting={sorting} title={getProposalTypeLabel(item.title)} /> - {index < mappedData.length - 1 && ( - + {index < proposals.length - 1 && ( + )} ))} )} - ) : ( - - - ); }; diff --git a/govtool/frontend/src/components/organisms/Hero.tsx b/govtool/frontend/src/components/organisms/Hero.tsx index b29b64839..6ef7aa1b9 100644 --- a/govtool/frontend/src/components/organisms/Hero.tsx +++ b/govtool/frontend/src/components/organisms/Hero.tsx @@ -18,7 +18,8 @@ export const Hero = () => { const IMAGE_SIZE = screenWidth < 640 ? 300 : screenWidth < 860 ? 400 : 600; const onClickVotingPower = useCallback( - () => openInNewTab("https://docs.sanchogov.tools/faqs/what-is-voting-power"), + () => + openInNewTab("https://docs.sanchogov.tools/faqs/what-is-voting-power"), [], ); @@ -84,15 +85,20 @@ export const Hero = () => { screenWidth >= 1728 ? IMAGE_SIZE / 8 : screenWidth >= 1512 - ? -(IMAGE_SIZE / 12) - : screenWidth >= 860 - ? -(IMAGE_SIZE / 8) - : -(IMAGE_SIZE / 4) + ? -(IMAGE_SIZE / 12) + : screenWidth >= 860 + ? -(IMAGE_SIZE / 8) + : -(IMAGE_SIZE / 4) } top={-80} zIndex={-1} > - + hero
); diff --git a/govtool/frontend/src/components/organisms/HomeCards.tsx b/govtool/frontend/src/components/organisms/HomeCards.tsx index 5e53b7d18..8ea58947c 100644 --- a/govtool/frontend/src/components/organisms/HomeCards.tsx +++ b/govtool/frontend/src/components/organisms/HomeCards.tsx @@ -19,16 +19,18 @@ export const HomeCards = () => { }, [openModal]); const onClickLearnMoreAboutDelegation = useCallback( - () => openInNewTab( - "https://docs.sanchogov.tools/faqs/ways-to-use-your-voting-power", - ), + () => + openInNewTab( + "https://docs.sanchogov.tools/faqs/ways-to-use-your-voting-power", + ), [], ); const onClickLearnMoreAboutDRepRegistration = useCallback( - () => openInNewTab( - "https://docs.sanchogov.tools/faqs/what-does-it-mean-to-register-as-a-drep", - ), + () => + openInNewTab( + "https://docs.sanchogov.tools/faqs/what-does-it-mean-to-register-as-a-drep", + ), [], ); @@ -65,10 +67,10 @@ export const HomeCards = () => { screenWidth < 640 ? 2 : screenWidth < 1024 - ? 5 - : screenWidth < 1440 - ? 10 - : 34 + ? 5 + : screenWidth < 1440 + ? 10 + : 34 } rowGap={4.625} > diff --git a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStorageInformation.tsx b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStorageInformation.tsx new file mode 100644 index 000000000..238e0f0a8 --- /dev/null +++ b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStorageInformation.tsx @@ -0,0 +1,135 @@ +import { Dispatch, SetStateAction, useCallback } from "react"; +import { Box } from "@mui/material"; +import OpenInNewIcon from "@mui/icons-material/OpenInNew"; + +import { Button, Spacer, Typography } from "@atoms"; +import { ICONS } from "@consts"; +import { + useRegisterAsdRepForm, + useTranslation, + useScreenDimension, +} from "@hooks"; +import { Step } from "@molecules"; +import { BgCard, ControlledField } from "@organisms"; +import { URL_REGEX, openInNewTab } from "@utils"; + +type StorageInformationProps = { + setStep: Dispatch>; +}; + +export const DRepStorageInformation = ({ + setStep, +}: StorageInformationProps) => { + const { t } = useTranslation(); + const { + control, + errors, + isRegistrationAsDRepLoading, + registerAsDrep, + watch, + } = useRegisterAsdRepForm(); + const { screenWidth } = useScreenDimension(); + + // TODO: change on correct file name + const fileName = "fileName"; + + // TODO: Change link to correct + const openGuideAboutStoringInformation = useCallback( + () => openInNewTab("https://sancho.network/"), + [], + ); + + const isActionButtonDisabled = !watch("storingURL"); + + const onClickBack = useCallback(() => setStep(3), []); + + return ( + + + {t("registration.storingInformationTitle")} + + + + {t("registration.storingInformationDescription")} + + + } + sx={{ + width: "fit-content", + ml: screenWidth < 1024 ? 0 : 1.75, + mt: screenWidth < 1024 ? 1.5 : 0, + }} + variant="outlined" + > + {`${fileName}.jsonld`} + + } + label={t("registration.storingInformationStep1Label")} + componentsLayoutStyles={{ + alignItems: screenWidth < 1024 ? undefined : "center", + flexDirection: screenWidth < 1024 ? "column" : "row", + }} + stepNumber={1} + /> + + + + + } + label={t("registration.storingInformationStep3Label")} + stepNumber={3} + /> + + + ); +}; diff --git a/govtool/frontend/src/components/organisms/RegisterAsdRepStepThree.tsx b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStoreDataInfo.tsx similarity index 83% rename from govtool/frontend/src/components/organisms/RegisterAsdRepStepThree.tsx rename to govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStoreDataInfo.tsx index 6b5a3df4d..1e6839512 100644 --- a/govtool/frontend/src/components/organisms/RegisterAsdRepStepThree.tsx +++ b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStoreDataInfo.tsx @@ -3,29 +3,23 @@ import { Box, Link } from "@mui/material"; import { Spacer, Typography } from "@atoms"; import { - useRegisterAsdRepFormContext, useScreenDimension, useTranslation, + useRegisterAsdRepForm, } from "@hooks"; import { openInNewTab } from "@utils"; -import { BgCard, ControlledField } from "."; +import { BgCard, ControlledField } from ".."; -export const RegisterAsdRepStepThree = ({ +export const DRepStoreDataInfo = ({ setStep, }: { setStep: Dispatch>; }) => { const { t } = useTranslation(); const { isMobile } = useScreenDimension(); - const { - control, - errors, - isRegistrationAsDRepLoading, - resetField, - submitForm, - watch, - } = useRegisterAsdRepFormContext(); + const { control, errors, isRegistrationAsDRepLoading, resetField, watch } = + useRegisterAsdRepForm(); const onClickBackButton = () => { setStep(2); @@ -34,6 +28,8 @@ export const RegisterAsdRepStepThree = ({ const isContinueDisabled = !watch("storeData"); + const onClickContinue = () => setStep(4); + // TODO: Add link about store data when available const openLink = () => openInNewTab("https://sancho.network/get-started"); @@ -42,7 +38,7 @@ export const RegisterAsdRepStepThree = ({ actionButtonLabel={t("register")} isActionButtonDisabled={isContinueDisabled} isLoadingActionButton={isRegistrationAsDRepLoading} - onClickActionButton={submitForm} + onClickActionButton={onClickContinue} onClickBackButton={onClickBackButton} > diff --git a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RegisterAsDRepForm.tsx b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RegisterAsDRepForm.tsx new file mode 100644 index 000000000..d2295b79f --- /dev/null +++ b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RegisterAsDRepForm.tsx @@ -0,0 +1,152 @@ +import { Dispatch, SetStateAction, useCallback } from "react"; +import { useFieldArray } from "react-hook-form"; +import { Box } from "@mui/material"; +import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline"; + +import { Button, InfoText, Spacer, Typography } from "@atoms"; +import { + useRegisterAsdRepForm, + useScreenDimension, + useTranslation, +} from "@hooks"; +import { URL_REGEX } from "@utils"; + +import { BgCard, ControlledField } from ".."; +import { Placeholders } from "@/consts"; + +const MAX_NUMBER_OF_LINKS = 8; + +export const RegisterAsDRepForm = ({ + setStep, +}: { + setStep: Dispatch>; +}) => { + const { t } = useTranslation(); + const { isMobile } = useScreenDimension(); + const { control, errors, register, watch } = useRegisterAsdRepForm(); + const { + append, + fields: links, + remove, + } = useFieldArray({ + control, + name: "links", + }); + + const onClickContinue = () => setStep(3); + + const onClickBack = () => setStep(1); + + const addLink = useCallback(() => append({ link: "" }), [append]); + + const removeLink = useCallback((index: number) => remove(index), [remove]); + + const isContinueButtonDisabled = !watch("dRepName"); + + const renderLinks = useCallback( + () => + links.map((field, index) => ( + 1 ? ( + removeLink(index)} + /> + ) : null + } + key={field.id} + // prefer-template rule for that label makes no sense + // eslint-disable-next-line prefer-template + label={t("forms.link") + ` ${index + 1}`} + layoutStyles={{ mb: 3 }} + placeholder={Placeholders.LINK} + name={`links.${index}.link`} + rules={{ + pattern: { + value: URL_REGEX, + message: t("createGovernanceAction.fields.validations.url"), + }, + }} + /> + )), + [links], + ); + + return ( + + + + + {t("registration.dRepName")} + + + {t("registration.dRepNameDescription")} + + + + + + + + {t("registration.aboutYou")} + + + {t("registration.aboutYouDescription")} + + + + + + +

+ {t("registration.linksDescription")} + + {t("registration.maximumLinks")} + +

+ + {renderLinks()} + {links?.length < MAX_NUMBER_OF_LINKS ? ( + + ) : null} + +
+ ); +}; diff --git a/govtool/frontend/src/components/organisms/RegisterAsdRepStepOne.tsx b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RolesAndResponsibilities.tsx similarity index 78% rename from govtool/frontend/src/components/organisms/RegisterAsdRepStepOne.tsx rename to govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RolesAndResponsibilities.tsx index e1fde2b6a..da33bfc94 100644 --- a/govtool/frontend/src/components/organisms/RegisterAsdRepStepOne.tsx +++ b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RolesAndResponsibilities.tsx @@ -1,4 +1,4 @@ -import { Dispatch, SetStateAction, useCallback } from "react"; +import { Dispatch, SetStateAction } from "react"; import { Trans } from "react-i18next"; import { Link } from "@mui/material"; @@ -11,9 +11,9 @@ import { PROTOCOL_PARAMS_KEY, } from "@utils"; -import { BgCard } from "."; +import { BgCard } from ".."; -export const RegisterAsdRepStepOne = ({ +export const RolesAndResponsibilities = ({ onClickCancel, setStep, }: { @@ -25,12 +25,10 @@ export const RegisterAsdRepStepOne = ({ const deposit = getItemFromLocalStorage(PROTOCOL_PARAMS_KEY); - const onClickContinue = useCallback(() => setStep(2), []); + const onClickContinue = () => setStep(2); - const openLearnMoreAboutDrep = useCallback( - () => openInNewTab("https://sancho.network/roles/drep"), - [], - ); + const openLearnMoreAboutDrep = () => + openInNewTab("https://sancho.network/roles/drep"); return ( {t("registration.rolesAndResponsibilitiesTitle")} @@ -46,7 +44,7 @@ export const RegisterAsdRepStepOne = ({ { const [isLoading, setIsLoading] = useState(false); - const { - buildSignSubmitConwayCertTx, - buildDRepRegCert, - buildDRepUpdateCert, - voter, - } = useCardano(); + const { buildSignSubmitConwayCertTx, buildDRepRegCert, buildDRepUpdateCert } = + useCardano(); const navigate = useNavigate(); const { openModal, closeModal } = useModal(); const { t } = useTranslation(); + const { voter } = useGetVoterInfo(); const onRegister = useCallback(async () => { setIsLoading(true); @@ -29,8 +27,7 @@ export const RegisterAsSoleVoterBox = () => { : await buildDRepRegCert(); const result = await buildSignSubmitConwayCertTx({ certBuilder, - type: "soleVoterRegistration", - registrationType: "registration", + type: "registerAsSoleVoter", }); if (result) { openModal({ @@ -49,6 +46,7 @@ export const RegisterAsSoleVoterBox = () => { }, }); } + // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (e: any) { const errorMessage = e.info ? e.info : e; diff --git a/govtool/frontend/src/components/organisms/RegisterAsdRepStepTwo.tsx b/govtool/frontend/src/components/organisms/RegisterAsdRepStepTwo.tsx deleted file mode 100644 index a9766dcfb..000000000 --- a/govtool/frontend/src/components/organisms/RegisterAsdRepStepTwo.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { Dispatch, SetStateAction, useCallback } from "react"; -import { Box, Link } from "@mui/material"; - -import { Spacer, Typography } from "@atoms"; -import { - useScreenDimension, - useRegisterAsdRepFormContext, - useTranslation, -} from "@hooks"; -import { openInNewTab } from "@utils"; - -import { BgCard, ControlledField } from "."; - -interface Props { - setStep: Dispatch>; -} - -export const RegisterAsdRepStepTwo = ({ setStep }: Props) => { - const { t } = useTranslation(); - const { isMobile } = useScreenDimension(); - const { - control, errors, isContinueButtonDisabled, isSkipButton, - } = useRegisterAsdRepFormContext(); - - const onClickContinue = useCallback(() => setStep(3), []); - - const onClickBackButton = useCallback(() => setStep(1), []); - - return ( - - - {t("registration.optional")} - - - {t("registration.addInformationTitle")} - - - {t("registration.addInformationDescription")} - - - - - - openInNewTab( - "https://docs.sanchogov.tools/faqs/how-to-create-a-metadata-anchor", - )} - alignSelf="center" - my={5} - sx={{ cursor: "pointer" }} - > - - {t("forms.howCreateUrlAndHash")} - - - - - ); -}; diff --git a/govtool/frontend/src/components/organisms/RetireAsSoleVoterBox.tsx b/govtool/frontend/src/components/organisms/RetireAsSoleVoterBox.tsx index 6bfd68d78..f691d3d72 100644 --- a/govtool/frontend/src/components/organisms/RetireAsSoleVoterBox.tsx +++ b/govtool/frontend/src/components/organisms/RetireAsSoleVoterBox.tsx @@ -6,6 +6,7 @@ import { PATHS } from "@consts"; import { CenteredBoxBottomButtons } from "@molecules"; import { useCardano, useModal } from "@context"; import { RetireAsSoleVoterBoxContent } from "@organisms"; +import { useGetVoterInfo } from "@/hooks"; export const RetireAsSoleVoterBox = () => { const [isLoading, setIsLoading] = useState(false); @@ -18,17 +19,23 @@ export const RetireAsSoleVoterBox = () => { } = useCardano(); const { openModal, closeModal } = useModal(); const { t } = useTranslation(); + const { voter } = useGetVoterInfo(); const onRetire = useCallback(async () => { try { setIsLoading(true); const isPendingTx = isPendingTransaction(); if (isPendingTx) return; - const certBuilder = await buildDRepRetirementCert(); + if (!voter?.deposit) { + throw new Error("Can not fetch deposit"); + } + const certBuilder = await buildDRepRetirementCert( + voter?.deposit?.toString() + ); const result = await buildSignSubmitConwayCertTx({ certBuilder, - type: "soleVoterRegistration", - registrationType: "retirement", + type: "retireAsSoleVoter", + voterDeposit: voter?.deposit?.toString(), }); if (result) { openModal({ @@ -47,6 +54,7 @@ export const RetireAsSoleVoterBox = () => { }, }); } + // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { const errorMessage = error.info ? error.info : error; @@ -72,6 +80,7 @@ export const RetireAsSoleVoterBox = () => { buildSignSubmitConwayCertTx, isPendingTransaction, openModal, + voter?.deposit, ]); return ( diff --git a/govtool/frontend/src/components/organisms/RetireAsSoleVoterBoxContent.tsx b/govtool/frontend/src/components/organisms/RetireAsSoleVoterBoxContent.tsx index bb371f0c4..983e0b81d 100644 --- a/govtool/frontend/src/components/organisms/RetireAsSoleVoterBoxContent.tsx +++ b/govtool/frontend/src/components/organisms/RetireAsSoleVoterBoxContent.tsx @@ -2,14 +2,13 @@ import { Link } from "@mui/material"; import { Trans } from "react-i18next"; import { Typography } from "@atoms"; -import { useScreenDimension, useTranslation } from "@hooks"; -import { correctAdaFormat, openInNewTab } from "@/utils"; -import { useCardano } from "@/context"; +import { useGetVoterInfo, useScreenDimension, useTranslation } from "@hooks"; +import { correctAdaFormat, openInNewTab } from "@utils"; export const RetireAsSoleVoterBoxContent = () => { const { isMobile } = useScreenDimension(); const { t } = useTranslation(); - const { voter } = useCardano(); + const { voter } = useGetVoterInfo(); return ( <> diff --git a/govtool/frontend/src/components/organisms/Slider.tsx b/govtool/frontend/src/components/organisms/Slider.tsx index ab2ca5969..577ca56a6 100644 --- a/govtool/frontend/src/components/organisms/Slider.tsx +++ b/govtool/frontend/src/components/organisms/Slider.tsx @@ -1,4 +1,4 @@ -import { useEffect, useMemo } from "react"; +import { useCallback, useEffect, useMemo } from "react"; import { Box, Link, Typography } from "@mui/material"; import { KeenSliderOptions } from "keen-slider"; import "keen-slider/keen-slider.min.css"; @@ -38,7 +38,7 @@ export const Slider = ({ }: SliderProps) => { const { isMobile, screenWidth, pagePadding } = useScreenDimension(); const navigate = useNavigate(); - const { voteTransaction } = useCardano(); + const { pendingTransaction } = useCardano(); const { t } = useTranslation(); const DEFAULT_SLIDER_CONFIG = { @@ -75,14 +75,31 @@ export const Slider = ({ useEffect(() => { refresh(); - }, [filters, sorting, searchPhrase, voteTransaction?.proposalId, data]); + }, [filters, sorting, searchPhrase, pendingTransaction.vote?.resourceId, data]); - const rangeSliderCalculationElement = dataLength < notSlicedDataLength - ? (screenWidth - + (onDashboard ? -290 - paddingOffset : -paddingOffset + 250)) - / 437 - : (screenWidth + (onDashboard ? -280 - paddingOffset : -paddingOffset)) - / 402; + const rangeSliderCalculationElement = + dataLength < notSlicedDataLength + ? (screenWidth + + (onDashboard ? -290 - paddingOffset : -paddingOffset + 250)) / + 437 + : (screenWidth + (onDashboard ? -280 - paddingOffset : -paddingOffset)) / + 402; + + const handleLinkPress = useCallback(() => { + if (onDashboard) { + navigate( + generatePath(PATHS.dashboardGovernanceActionsCategory, { + category: navigateKey, + }), + ); + } else { + navigate( + generatePath(PATHS.governanceActionsCategory, { + category: navigateKey, + }), + ); + } + }, [onDashboard]); return ( @@ -100,17 +117,7 @@ export const Slider = ({ display: "flex", }, ]} - onClick={() => (onDashboard - ? navigate( - generatePath(PATHS.dashboardGovernanceActionsCategory, { - category: navigateKey, - }), - ) - : navigate( - generatePath(PATHS.governanceActionsCategory, { - category: navigateKey, - }), - ))} + onClick={handleLinkPress} > (onDashboard - ? navigate( - generatePath(PATHS.dashboardGovernanceActionsCategory, { - category: navigateKey, - }), - ) - : navigate( - generatePath(PATHS.governanceActionsCategory, { - category: navigateKey, - }), - ))} + onClick={handleLinkPress} > {t("slider.viewAll")} diff --git a/govtool/frontend/src/components/organisms/StatusModal.tsx b/govtool/frontend/src/components/organisms/StatusModal.tsx index 122e07a01..5d813e73a 100644 --- a/govtool/frontend/src/components/organisms/StatusModal.tsx +++ b/govtool/frontend/src/components/organisms/StatusModal.tsx @@ -35,8 +35,8 @@ export const StatusModal = () => { state?.status === "warning" ? IMAGES.warningImage : state?.status === "success" - ? IMAGES.successImage - : ICONS.timerIcon + ? IMAGES.successImage + : ICONS.timerIcon } style={{ height: "84px", margin: "0 auto", width: "84px" }} /> diff --git a/govtool/frontend/src/components/organisms/TopNav.tsx b/govtool/frontend/src/components/organisms/TopNav.tsx index 17e06c024..ecfed9932 100644 --- a/govtool/frontend/src/components/organisms/TopNav.tsx +++ b/govtool/frontend/src/components/organisms/TopNav.tsx @@ -1,14 +1,10 @@ import { useEffect, useState } from "react"; import { NavLink, useNavigate } from "react-router-dom"; -import { - AppBar, Box, Grid, IconButton, -} from "@mui/material"; +import { AppBar, Box, Grid, IconButton } from "@mui/material"; import MenuIcon from "@mui/icons-material/Menu"; import { Button, Link } from "@atoms"; -import { - ICONS, IMAGES, PATHS, NAV_ITEMS, -} from "@consts"; +import { ICONS, IMAGES, PATHS, NAV_ITEMS } from "@consts"; import { useCardano, useModal } from "@context"; import { useScreenDimension, useTranslation } from "@hooks"; import { openInNewTab } from "@utils"; @@ -54,8 +50,8 @@ export const TopNav = ({ isConnectButton = true }) => { windowScroll > POSITION_TO_BLUR ? "rgba(256, 256, 256, 0.7)" : isMobile - ? "white" - : "transparent", + ? "white" + : "transparent", borderBottom: isMobile ? 1 : 0, borderColor: "lightblue", borderRadius: 0, @@ -82,7 +78,11 @@ export const TopNav = ({ isConnectButton = true }) => { onClick={() => (isConnectButton ? {} : disconnectWallet())} to={PATHS.home} > - + app-logo {screenWidth >= 1024 ? (