From 2fe023ae50d00d31e5cc90ddd05bd34c9bfa9e75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Placzy=C5=84ski?= Date: Thu, 7 Mar 2024 06:54:28 +0100 Subject: [PATCH 01/18] [#188] Remove outdated Nix configurations for frontend Removed obsolete Nix configuration files (default.nix, node-env.nix, and node-packages.nix) from the frontend module. This change is part of a broader effort to update and realign the frontend's build and deployment process with the backend's, ensuring a more streamlined and optimized development environment. The removal of these files paves the way for introducing a revised and more efficient Nix setup that better mirrors the backend's configuration and fulfills the specified acceptance criteria for development environments and dependencies. --- govtool/frontend/default.nix | 17 - govtool/frontend/node-env.nix | 689 ------ govtool/frontend/node-packages.nix | 3703 ---------------------------- 3 files changed, 4409 deletions(-) delete mode 100644 govtool/frontend/default.nix delete mode 100644 govtool/frontend/node-env.nix delete mode 100644 govtool/frontend/node-packages.nix diff --git a/govtool/frontend/default.nix b/govtool/frontend/default.nix deleted file mode 100644 index d9c0daef7..000000000 --- a/govtool/frontend/default.nix +++ /dev/null @@ -1,17 +0,0 @@ -# This file has been generated by node2nix 1.11.1. Do not edit! - -{pkgs ? import { - inherit system; - }, system ? builtins.currentSystem, nodejs ? pkgs."nodejs_18"}: - -let - nodeEnv = import ./node-env.nix { - inherit (pkgs) stdenv lib python2 runCommand writeTextFile writeShellScript; - inherit pkgs nodejs; - libtool = if pkgs.stdenv.isDarwin then pkgs.darwin.cctools else null; - }; -in -import ./node-packages.nix { - inherit (pkgs) fetchurl nix-gitignore stdenv lib fetchgit; - inherit nodeEnv; -} diff --git a/govtool/frontend/node-env.nix b/govtool/frontend/node-env.nix deleted file mode 100644 index bc1e36628..000000000 --- a/govtool/frontend/node-env.nix +++ /dev/null @@ -1,689 +0,0 @@ -# This file originates from node2nix - -{lib, stdenv, nodejs, python2, pkgs, libtool, runCommand, writeTextFile, writeShellScript}: - -let - # Workaround to cope with utillinux in Nixpkgs 20.09 and util-linux in Nixpkgs master - utillinux = if pkgs ? utillinux then pkgs.utillinux else pkgs.util-linux; - - python = if nodejs ? python then nodejs.python else python2; - - # Create a tar wrapper that filters all the 'Ignoring unknown extended header keyword' noise - tarWrapper = runCommand "tarWrapper" {} '' - mkdir -p $out/bin - - cat > $out/bin/tar <> $out/nix-support/hydra-build-products - ''; - }; - - # Common shell logic - installPackage = writeShellScript "install-package" '' - installPackage() { - local packageName=$1 src=$2 - - local strippedName - - local DIR=$PWD - cd $TMPDIR - - unpackFile $src - - # Make the base dir in which the target dependency resides first - mkdir -p "$(dirname "$DIR/$packageName")" - - if [ -f "$src" ] - then - # Figure out what directory has been unpacked - packageDir="$(find . -maxdepth 1 -type d | tail -1)" - - # Restore write permissions to make building work - find "$packageDir" -type d -exec chmod u+x {} \; - chmod -R u+w "$packageDir" - - # Move the extracted tarball into the output folder - mv "$packageDir" "$DIR/$packageName" - elif [ -d "$src" ] - then - # Get a stripped name (without hash) of the source directory. - # On old nixpkgs it's already set internally. - if [ -z "$strippedName" ] - then - strippedName="$(stripHash $src)" - fi - - # Restore write permissions to make building work - chmod -R u+w "$strippedName" - - # Move the extracted directory into the output folder - mv "$strippedName" "$DIR/$packageName" - fi - - # Change to the package directory to install dependencies - cd "$DIR/$packageName" - } - ''; - - # Bundle the dependencies of the package - # - # Only include dependencies if they don't exist. They may also be bundled in the package. - includeDependencies = {dependencies}: - lib.optionalString (dependencies != []) ( - '' - mkdir -p node_modules - cd node_modules - '' - + (lib.concatMapStrings (dependency: - '' - if [ ! -e "${dependency.packageName}" ]; then - ${composePackage dependency} - fi - '' - ) dependencies) - + '' - cd .. - '' - ); - - # Recursively composes the dependencies of a package - composePackage = { name, packageName, src, dependencies ? [], ... }@args: - builtins.addErrorContext "while evaluating node package '${packageName}'" '' - installPackage "${packageName}" "${src}" - ${includeDependencies { inherit dependencies; }} - cd .. - ${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."} - ''; - - pinpointDependencies = {dependencies, production}: - let - pinpointDependenciesFromPackageJSON = writeTextFile { - name = "pinpointDependencies.js"; - text = '' - var fs = require('fs'); - var path = require('path'); - - function resolveDependencyVersion(location, name) { - if(location == process.env['NIX_STORE']) { - return null; - } else { - var dependencyPackageJSON = path.join(location, "node_modules", name, "package.json"); - - if(fs.existsSync(dependencyPackageJSON)) { - var dependencyPackageObj = JSON.parse(fs.readFileSync(dependencyPackageJSON)); - - if(dependencyPackageObj.name == name) { - return dependencyPackageObj.version; - } - } else { - return resolveDependencyVersion(path.resolve(location, ".."), name); - } - } - } - - function replaceDependencies(dependencies) { - if(typeof dependencies == "object" && dependencies !== null) { - for(var dependency in dependencies) { - var resolvedVersion = resolveDependencyVersion(process.cwd(), dependency); - - if(resolvedVersion === null) { - process.stderr.write("WARNING: cannot pinpoint dependency: "+dependency+", context: "+process.cwd()+"\n"); - } else { - dependencies[dependency] = resolvedVersion; - } - } - } - } - - /* Read the package.json configuration */ - var packageObj = JSON.parse(fs.readFileSync('./package.json')); - - /* Pinpoint all dependencies */ - replaceDependencies(packageObj.dependencies); - if(process.argv[2] == "development") { - replaceDependencies(packageObj.devDependencies); - } - else { - packageObj.devDependencies = {}; - } - replaceDependencies(packageObj.optionalDependencies); - replaceDependencies(packageObj.peerDependencies); - - /* Write the fixed package.json file */ - fs.writeFileSync("package.json", JSON.stringify(packageObj, null, 2)); - ''; - }; - in - '' - node ${pinpointDependenciesFromPackageJSON} ${if production then "production" else "development"} - - ${lib.optionalString (dependencies != []) - '' - if [ -d node_modules ] - then - cd node_modules - ${lib.concatMapStrings (dependency: pinpointDependenciesOfPackage dependency) dependencies} - cd .. - fi - ''} - ''; - - # Recursively traverses all dependencies of a package and pinpoints all - # dependencies in the package.json file to the versions that are actually - # being used. - - pinpointDependenciesOfPackage = { packageName, dependencies ? [], production ? true, ... }@args: - '' - if [ -d "${packageName}" ] - then - cd "${packageName}" - ${pinpointDependencies { inherit dependencies production; }} - cd .. - ${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."} - fi - ''; - - # Extract the Node.js source code which is used to compile packages with - # native bindings - nodeSources = runCommand "node-sources" {} '' - tar --no-same-owner --no-same-permissions -xf ${nodejs.src} - mv node-* $out - ''; - - # Script that adds _integrity fields to all package.json files to prevent NPM from consulting the cache (that is empty) - addIntegrityFieldsScript = writeTextFile { - name = "addintegrityfields.js"; - text = '' - var fs = require('fs'); - var path = require('path'); - - function augmentDependencies(baseDir, dependencies) { - for(var dependencyName in dependencies) { - var dependency = dependencies[dependencyName]; - - // Open package.json and augment metadata fields - var packageJSONDir = path.join(baseDir, "node_modules", dependencyName); - var packageJSONPath = path.join(packageJSONDir, "package.json"); - - if(fs.existsSync(packageJSONPath)) { // Only augment packages that exist. Sometimes we may have production installs in which development dependencies can be ignored - console.log("Adding metadata fields to: "+packageJSONPath); - var packageObj = JSON.parse(fs.readFileSync(packageJSONPath)); - - if(dependency.integrity) { - packageObj["_integrity"] = dependency.integrity; - } else { - packageObj["_integrity"] = "sha1-000000000000000000000000000="; // When no _integrity string has been provided (e.g. by Git dependencies), add a dummy one. It does not seem to harm and it bypasses downloads. - } - - if(dependency.resolved) { - packageObj["_resolved"] = dependency.resolved; // Adopt the resolved property if one has been provided - } else { - packageObj["_resolved"] = dependency.version; // Set the resolved version to the version identifier. This prevents NPM from cloning Git repositories. - } - - if(dependency.from !== undefined) { // Adopt from property if one has been provided - packageObj["_from"] = dependency.from; - } - - fs.writeFileSync(packageJSONPath, JSON.stringify(packageObj, null, 2)); - } - - // Augment transitive dependencies - if(dependency.dependencies !== undefined) { - augmentDependencies(packageJSONDir, dependency.dependencies); - } - } - } - - if(fs.existsSync("./package-lock.json")) { - var packageLock = JSON.parse(fs.readFileSync("./package-lock.json")); - - if(![1, 2].includes(packageLock.lockfileVersion)) { - process.stderr.write("Sorry, I only understand lock file versions 1 and 2!\n"); - process.exit(1); - } - - if(packageLock.dependencies !== undefined) { - augmentDependencies(".", packageLock.dependencies); - } - } - ''; - }; - - # Reconstructs a package-lock file from the node_modules/ folder structure and package.json files with dummy sha1 hashes - reconstructPackageLock = writeTextFile { - name = "reconstructpackagelock.js"; - text = '' - var fs = require('fs'); - var path = require('path'); - - var packageObj = JSON.parse(fs.readFileSync("package.json")); - - var lockObj = { - name: packageObj.name, - version: packageObj.version, - lockfileVersion: 2, - requires: true, - packages: { - "": { - name: packageObj.name, - version: packageObj.version, - license: packageObj.license, - bin: packageObj.bin, - dependencies: packageObj.dependencies, - engines: packageObj.engines, - optionalDependencies: packageObj.optionalDependencies - } - }, - dependencies: {} - }; - - function augmentPackageJSON(filePath, packages, dependencies) { - var packageJSON = path.join(filePath, "package.json"); - if(fs.existsSync(packageJSON)) { - var packageObj = JSON.parse(fs.readFileSync(packageJSON)); - packages[filePath] = { - version: packageObj.version, - integrity: "sha1-000000000000000000000000000=", - dependencies: packageObj.dependencies, - engines: packageObj.engines, - optionalDependencies: packageObj.optionalDependencies - }; - dependencies[packageObj.name] = { - version: packageObj.version, - integrity: "sha1-000000000000000000000000000=", - dependencies: {} - }; - processDependencies(path.join(filePath, "node_modules"), packages, dependencies[packageObj.name].dependencies); - } - } - - function processDependencies(dir, packages, dependencies) { - if(fs.existsSync(dir)) { - var files = fs.readdirSync(dir); - - files.forEach(function(entry) { - var filePath = path.join(dir, entry); - var stats = fs.statSync(filePath); - - if(stats.isDirectory()) { - if(entry.substr(0, 1) == "@") { - // When we encounter a namespace folder, augment all packages belonging to the scope - var pkgFiles = fs.readdirSync(filePath); - - pkgFiles.forEach(function(entry) { - if(stats.isDirectory()) { - var pkgFilePath = path.join(filePath, entry); - augmentPackageJSON(pkgFilePath, packages, dependencies); - } - }); - } else { - augmentPackageJSON(filePath, packages, dependencies); - } - } - }); - } - } - - processDependencies("node_modules", lockObj.packages, lockObj.dependencies); - - fs.writeFileSync("package-lock.json", JSON.stringify(lockObj, null, 2)); - ''; - }; - - # Script that links bins defined in package.json to the node_modules bin directory - # NPM does not do this for top-level packages itself anymore as of v7 - linkBinsScript = writeTextFile { - name = "linkbins.js"; - text = '' - var fs = require('fs'); - var path = require('path'); - - var packageObj = JSON.parse(fs.readFileSync("package.json")); - - var nodeModules = Array(packageObj.name.split("/").length).fill("..").join(path.sep); - - if(packageObj.bin !== undefined) { - fs.mkdirSync(path.join(nodeModules, ".bin")) - - if(typeof packageObj.bin == "object") { - Object.keys(packageObj.bin).forEach(function(exe) { - if(fs.existsSync(packageObj.bin[exe])) { - console.log("linking bin '" + exe + "'"); - fs.symlinkSync( - path.join("..", packageObj.name, packageObj.bin[exe]), - path.join(nodeModules, ".bin", exe) - ); - } - else { - console.log("skipping non-existent bin '" + exe + "'"); - } - }) - } - else { - if(fs.existsSync(packageObj.bin)) { - console.log("linking bin '" + packageObj.bin + "'"); - fs.symlinkSync( - path.join("..", packageObj.name, packageObj.bin), - path.join(nodeModules, ".bin", packageObj.name.split("/").pop()) - ); - } - else { - console.log("skipping non-existent bin '" + packageObj.bin + "'"); - } - } - } - else if(packageObj.directories !== undefined && packageObj.directories.bin !== undefined) { - fs.mkdirSync(path.join(nodeModules, ".bin")) - - fs.readdirSync(packageObj.directories.bin).forEach(function(exe) { - if(fs.existsSync(path.join(packageObj.directories.bin, exe))) { - console.log("linking bin '" + exe + "'"); - fs.symlinkSync( - path.join("..", packageObj.name, packageObj.directories.bin, exe), - path.join(nodeModules, ".bin", exe) - ); - } - else { - console.log("skipping non-existent bin '" + exe + "'"); - } - }) - } - ''; - }; - - prepareAndInvokeNPM = {packageName, bypassCache, reconstructLock, npmFlags, production}: - let - forceOfflineFlag = if bypassCache then "--offline" else "--registry http://www.example.com"; - in - '' - # Pinpoint the versions of all dependencies to the ones that are actually being used - echo "pinpointing versions of dependencies..." - source $pinpointDependenciesScriptPath - - # Patch the shebangs of the bundled modules to prevent them from - # calling executables outside the Nix store as much as possible - patchShebangs . - - # Deploy the Node.js package by running npm install. Since the - # dependencies have been provided already by ourselves, it should not - # attempt to install them again, which is good, because we want to make - # it Nix's responsibility. If it needs to install any dependencies - # anyway (e.g. because the dependency parameters are - # incomplete/incorrect), it fails. - # - # The other responsibilities of NPM are kept -- version checks, build - # steps, postprocessing etc. - - export HOME=$TMPDIR - cd "${packageName}" - runHook preRebuild - - ${lib.optionalString bypassCache '' - ${lib.optionalString reconstructLock '' - if [ -f package-lock.json ] - then - echo "WARNING: Reconstruct lock option enabled, but a lock file already exists!" - echo "This will most likely result in version mismatches! We will remove the lock file and regenerate it!" - rm package-lock.json - else - echo "No package-lock.json file found, reconstructing..." - fi - - node ${reconstructPackageLock} - ''} - - node ${addIntegrityFieldsScript} - ''} - - npm ${forceOfflineFlag} --nodedir=${nodeSources} ${npmFlags} ${lib.optionalString production "--production"} rebuild - - runHook postRebuild - - if [ "''${dontNpmInstall-}" != "1" ] - then - # NPM tries to download packages even when they already exist if npm-shrinkwrap is used. - rm -f npm-shrinkwrap.json - - npm ${forceOfflineFlag} --nodedir=${nodeSources} --no-bin-links --ignore-scripts ${npmFlags} ${lib.optionalString production "--production"} install - fi - - # Link executables defined in package.json - node ${linkBinsScript} - ''; - - # Builds and composes an NPM package including all its dependencies - buildNodePackage = - { name - , packageName - , version ? null - , dependencies ? [] - , buildInputs ? [] - , production ? true - , npmFlags ? "" - , dontNpmInstall ? false - , bypassCache ? false - , reconstructLock ? false - , preRebuild ? "" - , dontStrip ? true - , unpackPhase ? "true" - , buildPhase ? "true" - , meta ? {} - , ... }@args: - - let - extraArgs = removeAttrs args [ "name" "dependencies" "buildInputs" "dontStrip" "dontNpmInstall" "preRebuild" "unpackPhase" "buildPhase" "meta" ]; - in - stdenv.mkDerivation ({ - name = "${name}${if version == null then "" else "-${version}"}"; - buildInputs = [ tarWrapper python nodejs ] - ++ lib.optional (stdenv.isLinux) utillinux - ++ lib.optional (stdenv.isDarwin) libtool - ++ buildInputs; - - inherit nodejs; - - inherit dontStrip; # Stripping may fail a build for some package deployments - inherit dontNpmInstall preRebuild unpackPhase buildPhase; - - compositionScript = composePackage args; - pinpointDependenciesScript = pinpointDependenciesOfPackage args; - - passAsFile = [ "compositionScript" "pinpointDependenciesScript" ]; - - installPhase = '' - source ${installPackage} - - # Create and enter a root node_modules/ folder - mkdir -p $out/lib/node_modules - cd $out/lib/node_modules - - # Compose the package and all its dependencies - source $compositionScriptPath - - ${prepareAndInvokeNPM { inherit packageName bypassCache reconstructLock npmFlags production; }} - - # Create symlink to the deployed executable folder, if applicable - if [ -d "$out/lib/node_modules/.bin" ] - then - ln -s $out/lib/node_modules/.bin $out/bin - - # Fixup all executables - ls $out/bin/* | while read i - do - file="$(readlink -f "$i")" - chmod u+rwx "$file" - if isScript "$file" - then - sed -i 's/\r$//' "$file" # convert crlf to lf - fi - done - fi - - # Create symlinks to the deployed manual page folders, if applicable - if [ -d "$out/lib/node_modules/${packageName}/man" ] - then - mkdir -p $out/share - for dir in "$out/lib/node_modules/${packageName}/man/"* - do - mkdir -p $out/share/man/$(basename "$dir") - for page in "$dir"/* - do - ln -s $page $out/share/man/$(basename "$dir") - done - done - fi - - # Run post install hook, if provided - runHook postInstall - ''; - - meta = { - # default to Node.js' platforms - platforms = nodejs.meta.platforms; - } // meta; - } // extraArgs); - - # Builds a node environment (a node_modules folder and a set of binaries) - buildNodeDependencies = - { name - , packageName - , version ? null - , src - , dependencies ? [] - , buildInputs ? [] - , production ? true - , npmFlags ? "" - , dontNpmInstall ? false - , bypassCache ? false - , reconstructLock ? false - , dontStrip ? true - , unpackPhase ? "true" - , buildPhase ? "true" - , ... }@args: - - let - extraArgs = removeAttrs args [ "name" "dependencies" "buildInputs" ]; - in - stdenv.mkDerivation ({ - name = "node-dependencies-${name}${if version == null then "" else "-${version}"}"; - - buildInputs = [ tarWrapper python nodejs ] - ++ lib.optional (stdenv.isLinux) utillinux - ++ lib.optional (stdenv.isDarwin) libtool - ++ buildInputs; - - inherit dontStrip; # Stripping may fail a build for some package deployments - inherit dontNpmInstall unpackPhase buildPhase; - - includeScript = includeDependencies { inherit dependencies; }; - pinpointDependenciesScript = pinpointDependenciesOfPackage args; - - passAsFile = [ "includeScript" "pinpointDependenciesScript" ]; - - installPhase = '' - source ${installPackage} - - mkdir -p $out/${packageName} - cd $out/${packageName} - - source $includeScriptPath - - # Create fake package.json to make the npm commands work properly - cp ${src}/package.json . - chmod 644 package.json - ${lib.optionalString bypassCache '' - if [ -f ${src}/package-lock.json ] - then - cp ${src}/package-lock.json . - chmod 644 package-lock.json - fi - ''} - - # Go to the parent folder to make sure that all packages are pinpointed - cd .. - ${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."} - - ${prepareAndInvokeNPM { inherit packageName bypassCache reconstructLock npmFlags production; }} - - # Expose the executables that were installed - cd .. - ${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."} - - mv ${packageName} lib - ln -s $out/lib/node_modules/.bin $out/bin - ''; - } // extraArgs); - - # Builds a development shell - buildNodeShell = - { name - , packageName - , version ? null - , src - , dependencies ? [] - , buildInputs ? [] - , production ? true - , npmFlags ? "" - , dontNpmInstall ? false - , bypassCache ? false - , reconstructLock ? false - , dontStrip ? true - , unpackPhase ? "true" - , buildPhase ? "true" - , ... }@args: - - let - nodeDependencies = buildNodeDependencies args; - extraArgs = removeAttrs args [ "name" "dependencies" "buildInputs" "dontStrip" "dontNpmInstall" "unpackPhase" "buildPhase" ]; - in - stdenv.mkDerivation ({ - name = "node-shell-${name}${if version == null then "" else "-${version}"}"; - - buildInputs = [ python nodejs ] ++ lib.optional (stdenv.isLinux) utillinux ++ buildInputs; - buildCommand = '' - mkdir -p $out/bin - cat > $out/bin/shell < Date: Thu, 7 Mar 2024 11:22:13 +0100 Subject: [PATCH 02/18] [#188] Introduce yarn2nix based package definition for frontend This commit introduces a new yarn2nix based package definition for the frontend application. By adding this Nix expression, developers can easily incorporate the project into their nix-shell based environment. Changes: - Added a new file `govtool/frontend/default.nix` that defines the yarn2nix based package configuration for the frontend application "govtool-frontend". This file includes settings for the project name, source directory, package.json file, and yarn.lock file wrapped in the `mkYarnPackage` function, enabling developers to utilize the project seamlessly in their nix-shell based environments. --- govtool/frontend/default.nix | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 govtool/frontend/default.nix diff --git a/govtool/frontend/default.nix b/govtool/frontend/default.nix new file mode 100644 index 000000000..e96a00e56 --- /dev/null +++ b/govtool/frontend/default.nix @@ -0,0 +1,10 @@ +{ pkgs ? import {} }: +let + project = pkgs.mkYarnPackage { + name = "govtool-frontend"; + src = ./.; + packageJSON = ./package.json; + yarnLock = ./yarn.lock; + }; +in +project From 9c81c7ffc1d2d790d2d51b89e14d68fef135f1ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Placzy=C5=84ski?= Date: Mon, 11 Mar 2024 09:43:09 +0100 Subject: [PATCH 03/18] Use more descriptive name for the Docker Compose target This commit changes the target name from 'docker' to 'docker-compose' in the 'utils.mk' file under 'scripts/govtool' directory. This more descriptive name accurately reflects the purpose of the target, making it easier for developers to understand the functionality associated with it. Changes: - Renamed the target from 'docker' to 'docker-compose' in 'scripts/govtool/utils.mk' to improve clarity and reflect the actual usage of the target within the project. --- scripts/govtool/utils.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/govtool/utils.mk b/scripts/govtool/utils.mk index 7e72355ee..b69671472 100644 --- a/scripts/govtool/utils.mk +++ b/scripts/govtool/utils.mk @@ -3,8 +3,8 @@ ssh: export TERM=xterm-256color; \ ssh $(ssh_url) -.PHONY: docker -docker: +.PHONY: docker-compose +docker-compose: @:$(call check_defined, cardano_network) @:$(call check_defined, env) @:$(call check_defined, cmd) From efec63fdbc74d49ac56766ce4cee3182c830eccb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Placzy=C5=84ski?= Date: Mon, 11 Mar 2024 09:54:09 +0100 Subject: [PATCH 04/18] [#442] Use the same UID in the datasource definition as in the panel configuration Resolve the issue causing warnings about non-existing datasources in the Grafana dashboard by ensuring that the UIDs in the datasource definitions match the UIDs specified in the panel configurations. This commit updates the UID values in the Grafana configuration files to align them correctly and eliminate the warnings. Changes: - Update the UID value for the Loki datasource in the datasource.yml file to "a698987a-0cba-42a9-86aa-3ba65104ef72". - Update the UID values for the Loki datasource in the govtool.json file to "a698987a-0cba-42a9-86aa-3ba65104ef72" to match the expected value. --- .../templates/grafana-provisioning/dashboards/govtool.json | 6 +++--- .../grafana-provisioning/datasources/datasource.yml | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts/govtool/config/templates/grafana-provisioning/dashboards/govtool.json b/scripts/govtool/config/templates/grafana-provisioning/dashboards/govtool.json index 70b42f887..fdb8da4c7 100644 --- a/scripts/govtool/config/templates/grafana-provisioning/dashboards/govtool.json +++ b/scripts/govtool/config/templates/grafana-provisioning/dashboards/govtool.json @@ -864,7 +864,7 @@ { "datasource": { "type": "loki", - "uid": "b0d64f25-3bca-4cc1-b8f3-6b391d88b442" + "uid": "a698987a-0cba-42a9-86aa-3ba65104ef72" }, "description": "Trace of every deploy made on this machine. This is a good place to start when something goes wrong.", "gridPos": { @@ -888,7 +888,7 @@ { "datasource": { "type": "loki", - "uid": "b0d64f25-3bca-4cc1-b8f3-6b391d88b442" + "uid": "a698987a-0cba-42a9-86aa-3ba65104ef72" }, "editorMode": "builder", "expr": "{filename=\"/var/log/deployment.log\"}", @@ -916,6 +916,6 @@ "timezone": "", "title": "GovTool basic stats", "uid": "e3697f62-6a01-4596-b626-d46141441b32", - "version": 44, + "version": 50, "weekStart": "" } diff --git a/scripts/govtool/config/templates/grafana-provisioning/datasources/datasource.yml b/scripts/govtool/config/templates/grafana-provisioning/datasources/datasource.yml index e1461426c..7f9f0de58 100644 --- a/scripts/govtool/config/templates/grafana-provisioning/datasources/datasource.yml +++ b/scripts/govtool/config/templates/grafana-provisioning/datasources/datasource.yml @@ -11,6 +11,8 @@ deleteDatasources: datasources: # name of the datasource. Required - name: Prometheus + # datasource UID. + uid: PBFA97CFB590B2093 # datasource type. Required type: prometheus # access mode. direct or proxy. Required @@ -51,6 +53,8 @@ datasources: # name of the datasource. Required - name: Loki + # datasource UID. + uid: a698987a-0cba-42a9-86aa-3ba65104ef72 # datasource type. Required type: loki # access mode. direct or proxy. Required From aaedd40de601adb8d32d30b562bc7cd535440094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Placzy=C5=84ski?= Date: Mon, 11 Mar 2024 10:44:14 +0100 Subject: [PATCH 05/18] [#442] Remove redundant basicAuth field from datasource.yml The basicAuth field was unnecessary and redundant under the datasources section in datasource.yml. Removing this field ensures the configuration to always parse and be loaded and establishes a cleaner and more concise configuration file for datasource provisioning in Grafana. Changes: - Removed the basicAuth field that was previously present in the datasources section of datasource.yml. This field was not needed and did not serve any functional purpose. This removal simplifies the configuration file and eliminates redundancy. --- .../templates/grafana-provisioning/datasources/datasource.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/govtool/config/templates/grafana-provisioning/datasources/datasource.yml b/scripts/govtool/config/templates/grafana-provisioning/datasources/datasource.yml index 7f9f0de58..a4ea24d58 100644 --- a/scripts/govtool/config/templates/grafana-provisioning/datasources/datasource.yml +++ b/scripts/govtool/config/templates/grafana-provisioning/datasources/datasource.yml @@ -78,9 +78,6 @@ datasources: # enable/disable with credentials headers withCredentials: # mark as default datasource. Max one per org - # enable/disable basic auth - basicAuth: false - # mark as default datasource. Max one per org isDefault: false version: 1 # allow users to edit datasources from the UI. From 1df33efc0d8f73cacfa385fa89485a941be760b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Sworze=C5=84?= Date: Fri, 8 Mar 2024 15:26:06 +0100 Subject: [PATCH 06/18] [#365] governance action certs (info and treasury) --- govtool/frontend/src/context/wallet.tsx | 218 +++++++++++++++++++----- 1 file changed, 173 insertions(+), 45 deletions(-) diff --git a/govtool/frontend/src/context/wallet.tsx b/govtool/frontend/src/context/wallet.tsx index 1b29b1d5d..18f00d5de 100644 --- a/govtool/frontend/src/context/wallet.tsx +++ b/govtool/frontend/src/context/wallet.tsx @@ -40,6 +40,12 @@ import { VotingBuilder, VotingProcedure, StakeRegistration, + VotingProposalBuilder, + InfoAction, + VotingProposal, + GovernanceAction, + TreasuryWithdrawals, + TreasuryWithdrawalsAction, } from "@emurgo/cardano-serialization-lib-asmjs"; import { Buffer } from "buffer"; import { useNavigate } from "react-router-dom"; @@ -94,6 +100,18 @@ type TransactionHistoryItem = { export type DRepActionType = "retirement" | "registration" | "update" | ""; +type InfoProps = { + hash: string; + url: string; +}; + +type TreasuryProps = { + amount: string; + hash: string; + receivingAddress: string; + url: string; +}; + interface CardanoContext { address?: string; disconnectWallet: () => Promise; @@ -152,6 +170,12 @@ interface CardanoContext { isPendingTransaction: () => boolean; isDrepLoading: boolean; setIsDrepLoading: Dispatch>; + buildNewInfoGovernanceAction: ( + infoProps: InfoProps + ) => Promise; + buildTreasuryGovernanceAction: ( + treasuryProps: TreasuryProps + ) => Promise; } type Utxos = { @@ -1218,74 +1242,178 @@ function CardanoProvider(props: Props) { [dRepID] ); + // info action + const buildNewInfoGovernanceAction = useCallback( + async ({ hash, url }: InfoProps) => { + let govActionBuilder = VotingProposalBuilder.new(); + const govActionDeposit = + getItemFromLocalStorage(PROTOCOL_PARAMS_KEY).govActDeposit; + try { + const rewardAddress = await walletApi?.getRewardAddress(); + + if (!rewardAddress) { + throw new Error("Can not get reward address"); + } + + // Create new info action + const infoAction = InfoAction.new(); + const infoGovAct = GovernanceAction.new_info_action(infoAction); + // Create an anchor + const anchorURL = URL.new(url); + const anchorHash = AnchorDataHash.from_hex(hash); + const anchor = Anchor.new(anchorURL, anchorHash); + + const rewardAddr = RewardAddress.from_address( + Address.from_bech32(rewardAddress) + ); + + if (!rewardAddr) { + throw new Error("Can not convert address to reward address"); + } + + // Create voting proposal + const votingProposal = VotingProposal.new( + infoGovAct, + anchor, + rewardAddr, + BigNum.from_str(govActionDeposit) + ); + govActionBuilder.add(votingProposal); + + return govActionBuilder; + } catch (err) { + console.error(err); + } + }, + [] + ); + + // treasury action + const buildTreasuryGovernanceAction = useCallback( + async ({ amount, hash, receivingAddress, url }: TreasuryProps) => { + const govActionBuilder = VotingProposalBuilder.new(); + const govActionDeposit = + getItemFromLocalStorage(PROTOCOL_PARAMS_KEY).govActDeposit; + try { + const rewardAddress = await walletApi?.getRewardAddress(); + + if (!rewardAddress) { + throw new Error("Can not get reward address"); + } + + const treasuryTarget = RewardAddress.from_address( + Address.from_bech32(receivingAddress) + ); + + if (!treasuryTarget) throw new Error("Can not get tresasury target"); + + const myWithdrawal = BigNum.from_str(amount); + const withdrawals = TreasuryWithdrawals.new(); + withdrawals.insert(treasuryTarget, myWithdrawal); + // Create new treasury withdrawal gov act + const treasuryAction = TreasuryWithdrawalsAction.new(withdrawals); + const treasuryGovAct = + GovernanceAction.new_treasury_withdrawals_action(treasuryAction); + // Create an anchor + const anchorURL = URL.new(url); + const anchorHash = AnchorDataHash.from_hex(hash); + const anchor = Anchor.new(anchorURL, anchorHash); + + const rewardAddr = RewardAddress.from_address( + Address.from_bech32(rewardAddress) + ); + + if (!rewardAddr) + throw new Error("Can not convert address to reward address"); + // Create voting proposal + const votingProposal = VotingProposal.new( + treasuryGovAct, + anchor, + rewardAddr, + BigNum.from_str(govActionDeposit) + ); + govActionBuilder.add(votingProposal); + + return govActionBuilder; + } catch (err) { + console.error(err); + } + }, + [] + ); + const value = useMemo( () => ({ address, - enable, - voter, - isEnabled, - isMainnet, - disconnectWallet, - dRepID, - dRepIDBech32, - pubDRepKey, - stakeKey, - setVoter, - setStakeKey, - stakeKeys, - walletApi, - error, - delegatedDRepID, - setDelegatedDRepID, - buildSignSubmitConwayCertTx, buildDRepRegCert, - buildDRepUpdateCert, buildDRepRetirementCert, + buildDRepUpdateCert, + buildNewInfoGovernanceAction, + buildSignSubmitConwayCertTx, + buildTreasuryGovernanceAction, buildVote, buildVoteDelegationCert, - delegateTransaction, - registerTransaction, - soleVoterTransaction, + delegatedDRepID, delegateTo, - voteTransaction, - isPendingTransaction, - isDrepLoading, - setIsDrepLoading, - isEnableLoading, - }), - [ - address, - enable, - voter, - isEnabled, - isMainnet, + delegateTransaction, disconnectWallet, dRepID, dRepIDBech32, + enable, + error, + isDrepLoading, + isEnabled, + isEnableLoading, + isMainnet, + isPendingTransaction, pubDRepKey, - stakeKey, - setVoter, + registerTransaction, + setDelegatedDRepID, + setIsDrepLoading, setStakeKey, + setVoter, + soleVoterTransaction, + stakeKey, stakeKeys, + voter, + voteTransaction, walletApi, - error, - delegatedDRepID, - setDelegatedDRepID, - buildSignSubmitConwayCertTx, + }), + [ + address, buildDRepRegCert, - buildDRepUpdateCert, buildDRepRetirementCert, + buildDRepUpdateCert, + buildNewInfoGovernanceAction, + buildSignSubmitConwayCertTx, + buildTreasuryGovernanceAction, buildVote, buildVoteDelegationCert, + delegatedDRepID, + delegateTo, delegateTransaction, + disconnectWallet, + dRepID, + dRepIDBech32, + enable, + error, + isDrepLoading, + isEnabled, + isEnableLoading, + isMainnet, + isPendingTransaction, + pubDRepKey, registerTransaction, + setDelegatedDRepID, + setIsDrepLoading, + setStakeKey, + setVoter, soleVoterTransaction, - delegateTo, + stakeKey, + stakeKeys, + voter, voteTransaction, - isPendingTransaction, - isDrepLoading, - setIsDrepLoading, - isEnableLoading, + walletApi, ] ); From ac31c8236a64007b4effb6cdab29eeed16fe6fe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Sworze=C5=84?= Date: Fri, 8 Mar 2024 16:25:24 +0100 Subject: [PATCH 07/18] move generateAnchor to untils --- govtool/frontend/src/context/wallet.tsx | 42 +++++++------------ govtool/frontend/src/utils/generateAnchor.tsx | 9 ++++ govtool/frontend/src/utils/index.ts | 1 + 3 files changed, 25 insertions(+), 27 deletions(-) create mode 100644 govtool/frontend/src/utils/generateAnchor.tsx diff --git a/govtool/frontend/src/context/wallet.tsx b/govtool/frontend/src/context/wallet.tsx index 18f00d5de..d5eda8cf7 100644 --- a/govtool/frontend/src/context/wallet.tsx +++ b/govtool/frontend/src/context/wallet.tsx @@ -10,8 +10,6 @@ import { } from "react"; import { Address, - Anchor, - AnchorDataHash, BigNum, Certificate, CertificatesBuilder, @@ -33,7 +31,6 @@ import { TransactionUnspentOutput, TransactionUnspentOutputs, TransactionWitnessSet, - URL, Value, VoteDelegation, Voter, @@ -59,20 +56,21 @@ import { PATHS } from "@consts"; import { CardanoApiWallet, VoterInfo, Protocol } from "@models"; import type { StatusModalState } from "@organisms"; import { - getPubDRepID, - WALLET_LS_KEY, - DELEGATE_TRANSACTION_KEY, - REGISTER_TRANSACTION_KEY, + checkIsMaintenanceOn, DELEGATE_TO_KEY, - PROTOCOL_PARAMS_KEY, + DELEGATE_TRANSACTION_KEY, + generateAnchor, getItemFromLocalStorage, - setItemToLocalStorage, - removeItemFromLocalStorage, + getPubDRepID, openInNewTab, + PROTOCOL_PARAMS_KEY, + REGISTER_SOLE_VOTER_TRANSACTION_KEY, + REGISTER_TRANSACTION_KEY, + removeItemFromLocalStorage, SANCHO_INFO_KEY, + setItemToLocalStorage, VOTE_TRANSACTION_KEY, - checkIsMaintenanceOn, - REGISTER_SOLE_VOTER_TRANSACTION_KEY, + WALLET_LS_KEY, } from "@utils"; import { getEpochParams, getTransactionStatus } from "@services"; import { @@ -1096,9 +1094,7 @@ function CardanoProvider(props: Props) { let dRepRegCert; // If there is an anchor if (cip95MetadataURL && cip95MetadataHash) { - const url = URL.new(cip95MetadataURL); - const hash = AnchorDataHash.from_hex(cip95MetadataHash); - const anchor = Anchor.new(url, hash); + const anchor = generateAnchor(cip95MetadataURL, cip95MetadataHash); // Create cert object using one Ada as the deposit dRepRegCert = DrepRegistration.new_with_anchor( dRepCred, @@ -1141,9 +1137,7 @@ function CardanoProvider(props: Props) { let dRepUpdateCert; // If there is an anchor if (cip95MetadataURL && cip95MetadataHash) { - const url = URL.new(cip95MetadataURL); - const hash = AnchorDataHash.from_hex(cip95MetadataHash); - const anchor = Anchor.new(url, hash); + const anchor = generateAnchor(cip95MetadataURL, cip95MetadataHash); // Create cert object using one Ada as the deposit dRepUpdateCert = DrepUpdate.new_with_anchor(dRepCred, anchor); } else { @@ -1217,9 +1211,7 @@ function CardanoProvider(props: Props) { let votingProcedure; if (cip95MetadataURL && cip95MetadataHash) { - const url = URL.new(cip95MetadataURL); - const hash = AnchorDataHash.from_hex(cip95MetadataHash); - const anchor = Anchor.new(url, hash); + const anchor = generateAnchor(cip95MetadataURL, cip95MetadataHash); // Create cert object using one Ada as the deposit votingProcedure = VotingProcedure.new_with_anchor( votingChoice, @@ -1259,9 +1251,7 @@ function CardanoProvider(props: Props) { const infoAction = InfoAction.new(); const infoGovAct = GovernanceAction.new_info_action(infoAction); // Create an anchor - const anchorURL = URL.new(url); - const anchorHash = AnchorDataHash.from_hex(hash); - const anchor = Anchor.new(anchorURL, anchorHash); + const anchor = generateAnchor(url, hash); const rewardAddr = RewardAddress.from_address( Address.from_bech32(rewardAddress) @@ -1315,9 +1305,7 @@ function CardanoProvider(props: Props) { const treasuryGovAct = GovernanceAction.new_treasury_withdrawals_action(treasuryAction); // Create an anchor - const anchorURL = URL.new(url); - const anchorHash = AnchorDataHash.from_hex(hash); - const anchor = Anchor.new(anchorURL, anchorHash); + const anchor = generateAnchor(url, hash); const rewardAddr = RewardAddress.from_address( Address.from_bech32(rewardAddress) diff --git a/govtool/frontend/src/utils/generateAnchor.tsx b/govtool/frontend/src/utils/generateAnchor.tsx new file mode 100644 index 000000000..3f457e6e6 --- /dev/null +++ b/govtool/frontend/src/utils/generateAnchor.tsx @@ -0,0 +1,9 @@ +import {Anchor, AnchorDataHash, URL } from "@emurgo/cardano-serialization-lib-asmjs"; + +export const generateAnchor = (url: string, hash: string) => { + const metadataUrl = URL.new(url); + const urlHash = AnchorDataHash.from_hex(hash); + const anchor = Anchor.new(metadataUrl, urlHash); + + return anchor; +}; \ No newline at end of file diff --git a/govtool/frontend/src/utils/index.ts b/govtool/frontend/src/utils/index.ts index 08a870600..313353de7 100644 --- a/govtool/frontend/src/utils/index.ts +++ b/govtool/frontend/src/utils/index.ts @@ -4,6 +4,7 @@ export * from "./callAll"; export * from "./checkIsMaintenanceOn"; export * from "./checkIsWalletConnected"; export * from "./formatDate"; +export * from "./generateAnchor"; export * from "./getDRepID"; export * from "./getGovActionId"; export * from "./getLengthInBytes"; From 6e4151f0913c81039d81becb4541b444a41ce81a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Sworze=C5=84?= Date: Sat, 9 Mar 2024 03:17:44 +0100 Subject: [PATCH 08/18] add working logic to GA builders --- govtool/frontend/src/context/wallet.tsx | 58 ++++++++++--------------- govtool/frontend/src/models/wallet.ts | 2 +- 2 files changed, 24 insertions(+), 36 deletions(-) diff --git a/govtool/frontend/src/context/wallet.tsx b/govtool/frontend/src/context/wallet.tsx index d5eda8cf7..d1081d006 100644 --- a/govtool/frontend/src/context/wallet.tsx +++ b/govtool/frontend/src/context/wallet.tsx @@ -234,9 +234,9 @@ function CardanoProvider(props: Props) { { proposalId: string } & TransactionHistoryItem >({ time: undefined, transactionHash: "", proposalId: "" }); const [isDrepLoading, setIsDrepLoading] = useState(true); - const { addSuccessAlert, addWarningAlert, addErrorAlert } = useSnackbar(); const { t } = useTranslation(); + const epochParams = getItemFromLocalStorage(PROTOCOL_PARAMS_KEY); const isPendingTransaction = useCallback(() => { if ( @@ -1083,7 +1083,6 @@ function CardanoProvider(props: Props) { cip95MetadataHash?: string ): Promise => { try { - const epochParams = getItemFromLocalStorage(PROTOCOL_PARAMS_KEY); // Build DRep Registration Certificate const certBuilder = CertificatesBuilder.new(); @@ -1117,7 +1116,7 @@ function CardanoProvider(props: Props) { throw e; } }, - [dRepID] + [epochParams, dRepID] ); // conway alpha @@ -1234,39 +1233,39 @@ function CardanoProvider(props: Props) { [dRepID] ); + const getRewardAddress = useCallback(async () => { + const addresses = await walletApi?.getRewardAddresses(); + if (!addresses) { + throw new Error("Can not get reward addresses from wallet."); + } + const firstAddress = addresses[0]; + const bech32Address = Address.from_bytes( + Buffer.from(firstAddress, "hex") + ).to_bech32(); + + return RewardAddress.from_address(Address.from_bech32(bech32Address)); + }, [walletApi]); + // info action const buildNewInfoGovernanceAction = useCallback( async ({ hash, url }: InfoProps) => { let govActionBuilder = VotingProposalBuilder.new(); - const govActionDeposit = - getItemFromLocalStorage(PROTOCOL_PARAMS_KEY).govActDeposit; try { - const rewardAddress = await walletApi?.getRewardAddress(); - - if (!rewardAddress) { - throw new Error("Can not get reward address"); - } - // Create new info action const infoAction = InfoAction.new(); const infoGovAct = GovernanceAction.new_info_action(infoAction); // Create an anchor const anchor = generateAnchor(url, hash); - const rewardAddr = RewardAddress.from_address( - Address.from_bech32(rewardAddress) - ); - - if (!rewardAddr) { - throw new Error("Can not convert address to reward address"); - } + const rewardAddr = await getRewardAddress(); + if (!rewardAddr) throw new Error("Can not get reward address"); // Create voting proposal const votingProposal = VotingProposal.new( infoGovAct, anchor, rewardAddr, - BigNum.from_str(govActionDeposit) + BigNum.from_str(epochParams.gov_action_deposit.toString()) ); govActionBuilder.add(votingProposal); @@ -1275,22 +1274,14 @@ function CardanoProvider(props: Props) { console.error(err); } }, - [] + [epochParams] ); // treasury action const buildTreasuryGovernanceAction = useCallback( async ({ amount, hash, receivingAddress, url }: TreasuryProps) => { const govActionBuilder = VotingProposalBuilder.new(); - const govActionDeposit = - getItemFromLocalStorage(PROTOCOL_PARAMS_KEY).govActDeposit; try { - const rewardAddress = await walletApi?.getRewardAddress(); - - if (!rewardAddress) { - throw new Error("Can not get reward address"); - } - const treasuryTarget = RewardAddress.from_address( Address.from_bech32(receivingAddress) ); @@ -1307,18 +1298,15 @@ function CardanoProvider(props: Props) { // Create an anchor const anchor = generateAnchor(url, hash); - const rewardAddr = RewardAddress.from_address( - Address.from_bech32(rewardAddress) - ); + const rewardAddr = await getRewardAddress(); - if (!rewardAddr) - throw new Error("Can not convert address to reward address"); + if (!rewardAddr) throw new Error("Can not get reward address"); // Create voting proposal const votingProposal = VotingProposal.new( treasuryGovAct, anchor, rewardAddr, - BigNum.from_str(govActionDeposit) + BigNum.from_str(epochParams.gov_action_deposit.toString()) ); govActionBuilder.add(votingProposal); @@ -1327,7 +1315,7 @@ function CardanoProvider(props: Props) { console.error(err); } }, - [] + [epochParams] ); const value = useMemo( diff --git a/govtool/frontend/src/models/wallet.ts b/govtool/frontend/src/models/wallet.ts index ebba3d0c1..2baf480c2 100644 --- a/govtool/frontend/src/models/wallet.ts +++ b/govtool/frontend/src/models/wallet.ts @@ -94,7 +94,7 @@ export interface CardanoApiWallet { getUsedAddresses(): Promise; getUnusedAddresses(): Promise; getChangeAddress(): Promise; - getRewardAddress(): Promise; + getRewardAddresses(): Promise; getNetworkId(): Promise; signData(arg0: any, arg1?: any): Promise; signTx(arg0: any, arg1?: any): Promise; From 2c6e008c607b9f89f804782920485cc3476b3bdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Sworze=C5=84?= Date: Sat, 9 Mar 2024 08:04:15 +0100 Subject: [PATCH 09/18] add getRewardAddress to builders dependencies --- govtool/frontend/src/context/wallet.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/govtool/frontend/src/context/wallet.tsx b/govtool/frontend/src/context/wallet.tsx index d1081d006..2d72d4659 100644 --- a/govtool/frontend/src/context/wallet.tsx +++ b/govtool/frontend/src/context/wallet.tsx @@ -1274,7 +1274,7 @@ function CardanoProvider(props: Props) { console.error(err); } }, - [epochParams] + [epochParams, getRewardAddress] ); // treasury action @@ -1315,7 +1315,7 @@ function CardanoProvider(props: Props) { console.error(err); } }, - [epochParams] + [epochParams, getRewardAddress] ); const value = useMemo( From e925eabf2e81a519d088df2e57d2016ba1be0a48 Mon Sep 17 00:00:00 2001 From: jankun4 Date: Mon, 11 Mar 2024 21:40:53 +0100 Subject: [PATCH 10/18] [#446] extend drep/list endpoint drep/list endpoint now returns 2 additional fields (status and type), it returns retired dreps and it allows user to search for given drep by drep name --- CHANGELOG.md | 1 + govtool/backend/sql/list-dreps.sql | 45 ++++++++++++++-- govtool/backend/src/VVA/API.hs | 39 ++++++++++++-- govtool/backend/src/VVA/API/Types.hs | 77 +++++++++++++++++++++++++--- govtool/backend/src/VVA/DRep.hs | 14 ++++- govtool/backend/src/VVA/Types.hs | 14 ++++- govtool/status-service/app.py | 2 +- 7 files changed, 173 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 349a0646e..059c7167d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ changes. - Fixed CSP settings to allow error reports with Sentry [Issue 291](https://github.com/IntersectMBO/govtool/issues/291). ### Changed +- `drep/list` now return also `status` and `type` fields. Also it now returns the retired dreps, and you can search for given drep by name using optional query parameter. If the drep name is passed exactly, then you can even find a drep that's sole voter. [Issue 446](https://github.com/IntersectMBO/govtool/issues/446) - `drep/list` and `drep/info` endpoints now return additional data such as metadata url and hash, and voting power [Issue 223](https://github.com/IntersectMBO/govtool/issues/223) - `drep/info` now does not return sole voters (dreps without metadata) [Issue 317](https://github.com/IntersectMBO/govtool/issues/317) - `isRegistered` and `wasRegistered` fields in the drep/info endpoint changed to `isRegisteredAsDRep` and `wasRegisteredAsDRep` respectively [Issue 212](https://github.com/IntersectMBO/govtool/issues/212) diff --git a/govtool/backend/sql/list-dreps.sql b/govtool/backend/sql/list-dreps.sql index a0a0979ac..931ae6430 100644 --- a/govtool/backend/sql/list-dreps.sql +++ b/govtool/backend/sql/list-dreps.sql @@ -5,29 +5,64 @@ WITH DRepDistr AS ( FROM drep_distr JOIN drep_hash on drep_hash.id = drep_distr.hash_id +), DRepActivity AS ( + select + drep_activity as drep_activity, + epoch_no as epoch_no + from epoch_param + where epoch_no is not null + order by epoch_no desc + limit 1 ) SELECT encode(dh.raw, 'hex'), + dh.view, va.url, encode(va.data_hash, 'hex'), dr_deposit.deposit, - DRepDistr.amount + DRepDistr.amount, + (DRepActivity.epoch_no - Max(coalesce(block.epoch_no,block_first_register.epoch_no))) <= DRepActivity.drep_activity as active, + second_to_newest_drep_registration.voting_anchor_id is not null as has_voting_anchor FROM drep_hash dh JOIN ( SELECT dr.id, dr.drep_hash_id, dr.deposit, - ROW_NUMBER() OVER(PARTITION BY dr.drep_hash_id ORDER BY dr.id DESC) AS rn + ROW_NUMBER() OVER(PARTITION BY dr.drep_hash_id ORDER BY dr.tx_id DESC) AS rn FROM drep_registration dr - where dr.deposit > 0 + where dr.deposit is not null ) as dr_deposit on dr_deposit.drep_hash_id = dh.id and dr_deposit.rn = 1 LEFT JOIN ( SELECT dr.id, dr.drep_hash_id, dr.voting_anchor_id, - ROW_NUMBER() OVER(PARTITION BY dr.drep_hash_id ORDER BY dr.id DESC) AS rn + ROW_NUMBER() OVER(PARTITION BY dr.drep_hash_id ORDER BY dr.tx_id DESC) AS rn FROM drep_registration dr ) as dr_voting_anchor on dr_voting_anchor.drep_hash_id = dh.id and dr_voting_anchor.rn = 1 +LEFT JOIN ( + SELECT dr.id, dr.drep_hash_id, dr.voting_anchor_id, + ROW_NUMBER() OVER(PARTITION BY dr.drep_hash_id ORDER BY dr.tx_id DESC) AS rn + FROM drep_registration dr +) as second_to_newest_drep_registration +on second_to_newest_drep_registration.drep_hash_id = dh.id and second_to_newest_drep_registration.rn = 2 LEFT JOIN DRepDistr on DRepDistr.hash_id = dh.id and DRepDistr.rn = 1 -JOIN voting_anchor va ON va.id = dr_voting_anchor.voting_anchor_id +LEFT JOIN voting_anchor va ON va.id = dr_voting_anchor.voting_anchor_id +CROSS JOIN DRepActivity +LEFT JOIN voting_procedure as voting_procedure +on voting_procedure.drep_voter = dh.id +LEFT JOIN tx as tx +on tx.id = voting_procedure.tx_id +LEFT JOIN block as block +on block.id = tx.block_id +JOIN ( + SELECT dr.tx_id, dr.drep_hash_id, + ROW_NUMBER() OVER(PARTITION BY dr.drep_hash_id ORDER BY dr.tx_id ASC) AS rn + FROM drep_registration dr +) as dr_first_register +on dr_first_register.drep_hash_id = dh.id and dr_first_register.rn = 1 +JOIN tx as tx_first_register +on tx_first_register.id = dr_first_register.tx_id +JOIN block as block_first_register +ON block_first_register.id = tx_first_register.block_id +GROUP BY dh.raw, second_to_newest_drep_registration.voting_anchor_id, dh.view, va.url, va.data_hash, dr_deposit.deposit, DRepDistr.amount, DRepActivity.epoch_no, DRepActivity.drep_activity diff --git a/govtool/backend/src/VVA/API.hs b/govtool/backend/src/VVA/API.hs index a61317978..c303fe32f 100644 --- a/govtool/backend/src/VVA/API.hs +++ b/govtool/backend/src/VVA/API.hs @@ -35,7 +35,7 @@ import VVA.Network as Network import Numeric.Natural (Natural) type VVAApi = - "drep" :> "list" :> Get '[JSON] [DRep] + "drep" :> "list" :> QueryParam "drepView" Text :> Get '[JSON] [DRep] :<|> "drep" :> "get-voting-power" :> Capture "drepId" HexText :> Get '[JSON] Integer :<|> "drep" :> "getVotes" :> Capture "drepId" HexText :> QueryParams "type" GovernanceActionType :> QueryParam "sort" GovernanceActionSortMode :> Get '[JSON] [VoteResponse] :<|> "drep" :> "info" :> Capture "drepId" HexText :> Get '[JSON] DRepInfoResponse @@ -68,11 +68,40 @@ server = drepList :<|> throw500 :<|> getNetworkMetrics -drepList :: App m => m [DRep] -drepList = do + +mapDRepType :: Types.DRepType -> DRepType +mapDRepType Types.DRep = NormalDRep +mapDRepType Types.SoleVoter = SoleVoter + +mapDRepStatus :: Types.DRepStatus -> DRepStatus +mapDRepStatus Types.Retired = Retired +mapDRepStatus Types.Active = Active +mapDRepStatus Types.Inactive = Inactive + +drepRegistrationToDrep :: Types.DRepRegistration -> DRep +drepRegistrationToDrep Types.DRepRegistration {..} = + DRep + { dRepDrepId = DRepHash dRepRegistrationDRepHash, + dRepView = dRepRegistrationView, + dRepUrl = dRepRegistrationUrl, + dRepMetadataHash = dRepRegistrationDataHash, + dRepDeposit = dRepRegistrationDeposit, + dRepVotingPower = dRepRegistrationVotingPower, + dRepStatus = mapDRepStatus dRepRegistrationStatus, + dRepType = mapDRepType dRepRegistrationType + } + +drepList :: App m => Maybe Text -> m [DRep] +drepList mDRepView = do CacheEnv {dRepListCache} <- asks vvaCache - map (\(Types.DRepRegistration drep_hash url data_hash deposit votingPower) -> DRep (DRepHash drep_hash) url data_hash deposit votingPower) - <$> cacheRequest dRepListCache () DRep.listDReps + dreps <- cacheRequest dRepListCache () DRep.listDReps + let filtered = flip filter dreps $ \Types.DRepRegistration {..} -> + case (dRepRegistrationType, mDRepView) of + (Types.SoleVoter, Just x) -> x == dRepRegistrationView + (Types.DRep, Just x) -> isInfixOf x dRepRegistrationView + (Types.DRep, Nothing) -> True + _ -> False + return $ map drepRegistrationToDrep filtered getVotingPower :: App m => HexText -> m Integer getVotingPower (unHexText -> dRepId) = do diff --git a/govtool/backend/src/VVA/API/Types.hs b/govtool/backend/src/VVA/API/Types.hs index d5e24ee21..efc79857d 100644 --- a/govtool/backend/src/VVA/API/Types.hs +++ b/govtool/backend/src/VVA/API/Types.hs @@ -511,14 +511,66 @@ instance ToSchema DRepHash where ?~ toJSON exampleDrepHash +data DRepStatus = Retired | Active | Inactive + deriving (Generic, Show) +-- ToJSON instance for DRepStatus +instance ToJSON DRepStatus where + toJSON Retired = "Retired" + toJSON Active = "Active" + toJSON Inactive = "Inactive" + +-- FromJSON instance for DRepStatus +instance FromJSON DRepStatus where + parseJSON = withText "DRepStatus" $ \case + "Retired" -> pure Retired + "Active" -> pure Active + "Inactive" -> pure Inactive + _ -> fail "Invalid DRepStatus" + +-- ToSchema instance for DRepStatus +instance ToSchema DRepStatus where + declareNamedSchema _ = pure $ NamedSchema (Just "DRepStatus") $ mempty + & type_ ?~ OpenApiString + & description ?~ "DRep Status" + & enum_ ?~ map toJSON [Retired, Active, Inactive] + + + +data DRepType = NormalDRep | SoleVoter + +instance Show DRepType where + show NormalDRep = "DRep" + show SoleVoter = "SoleVoter" + +-- ToJSON instance for DRepType +instance ToJSON DRepType where + toJSON NormalDRep = "DRep" + toJSON SoleVoter = "SoleVoter" + +-- FromJSON instance for DRepType +instance FromJSON DRepType where + parseJSON = withText "DRepType" $ \case + "DRep" -> pure NormalDRep + "SoleVoter" -> pure SoleVoter + _ -> fail "Invalid DRepType" + +-- ToSchema instance for DRepType +instance ToSchema DRepType where + declareNamedSchema _ = pure $ NamedSchema (Just "DRepType") $ mempty + & type_ ?~ OpenApiString + & description ?~ "DRep Type" + & enum_ ?~ map toJSON [NormalDRep, SoleVoter] data DRep = DRep { dRepDrepId :: DRepHash + , dRepView :: Text , dRepUrl :: Maybe Text , dRepMetadataHash :: Maybe Text , dRepDeposit :: Integer , dRepVotingPower :: Maybe Integer + , dRepStatus :: DRepStatus + , dRepType :: DRepType } deriving (Generic, Show) @@ -527,17 +579,30 @@ deriveJSON (jsonOptions "dRep") ''DRep exampleDrep :: Text exampleDrep = "{\"drepId\": \"d3a62ffe9c214e1a6a9809f7ab2a104c117f85e1f171f8f839d94be5\"," + <> "\"view\": \"drep1l8uyy66sm8u82h82gc8hkcy2xu24dl8ffsh58aa0v7d37yp48u8\"," <> "\"url\": \"https://proposal.metadata.xyz\"," <> "\"metadataHash\": \"9af10e89979e51b8cdc827c963124a1ef4920d1253eef34a1d5cfe76438e3f11\"," <> "\"deposit\": 0," - <> "\"votingPower\": 0}" + <> "\"votingPower\": 0," + <> "\"status\": \"Active\"," + <> "\"type\": \"DRep\"}" +-- ToSchema instance for DRep instance ToSchema DRep where - declareNamedSchema _ = pure $ NamedSchema (Just "DRep") $ mempty - & type_ ?~ OpenApiObject - & description ?~ "DRep" - & example - ?~ toJSON exampleDrep + declareNamedSchema proxy = do + NamedSchema name_ schema_ <- + genericDeclareNamedSchema + ( fromAesonOptions $ jsonOptions "dRep" ) + proxy + return $ + NamedSchema name_ $ + schema_ + & description ?~ "DRep" + & example + ?~ toJSON exampleDrep + + + data GetNetworkMetricsResponse = GetNetworkMetricsResponse { getNetworkMetricsResponseCurrentTime :: UTCTime diff --git a/govtool/backend/src/VVA/DRep.hs b/govtool/backend/src/VVA/DRep.hs index b0b719402..211119a8e 100644 --- a/govtool/backend/src/VVA/DRep.hs +++ b/govtool/backend/src/VVA/DRep.hs @@ -31,6 +31,8 @@ import VVA.Types , Proposal(..) , Vote(..) , DRepInfo(..) + , DRepType(..) + , DRepStatus(..) ) @@ -60,7 +62,17 @@ listDReps :: m [DRepRegistration] listDReps = withPool $ \conn -> do results <- liftIO $ SQL.query_ conn listDRepsSql - return [DRepRegistration drepHash url dataHash (floor @Scientific deposit) votingPower | (drepHash, url, dataHash, deposit, votingPower) <- results] + return + [ DRepRegistration drepHash drepView url dataHash (floor @Scientific deposit) votingPower status drepType + | (drepHash, drepView, url, dataHash, deposit, votingPower, isActive, wasDRep) <- results + , let status = case (isActive, deposit) of + (_, d) | d < 0 -> Retired + (isActive, d) | d >= 0 && isActive -> Active + | d >= 0 && not isActive -> Inactive + , let drepType | url == Nothing && wasDRep = DRep + | url == Nothing && not wasDRep = SoleVoter + | url /= Nothing = DRep + ] getVotesSql :: SQL.Query getVotesSql = sqlFrom $(embedFile "sql/get-votes.sql") diff --git a/govtool/backend/src/VVA/Types.hs b/govtool/backend/src/VVA/Types.hs index c63ac8b79..e316f34e0 100644 --- a/govtool/backend/src/VVA/Types.hs +++ b/govtool/backend/src/VVA/Types.hs @@ -69,12 +69,24 @@ data DRepInfo = DRepInfo , dRepInfoVotingPower :: Maybe Integer } +data DRepStatus + = Retired + | Active + | Inactive + +data DRepType + = DRep + | SoleVoter + data DRepRegistration = DRepRegistration - { dRepRegistrationDrepHash :: Text + { dRepRegistrationDRepHash :: Text + , dRepRegistrationView :: Text , dRepRegistrationUrl :: Maybe Text , dRepRegistrationDataHash :: Maybe Text , dRepRegistrationDeposit :: Integer , dRepRegistrationVotingPower :: Maybe Integer + , dRepRegistrationStatus :: DRepStatus + , dRepRegistrationType :: DRepType } data Proposal = Proposal diff --git a/govtool/status-service/app.py b/govtool/status-service/app.py index a453218f4..30942ac1c 100644 --- a/govtool/status-service/app.py +++ b/govtool/status-service/app.py @@ -7,7 +7,7 @@ GRAFANA_PASSWORD = environ['GRAFANA_PASSWORD'] alert_health_mapping = { - 'inactive': 'healthy', + 'Inactive': 'healthy', 'pending': 'warning', 'firing': 'not healthy' } From f060578cf0dc9aa80528567c45ca5abd1737711d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Placzy=C5=84ski?= Date: Tue, 12 Mar 2024 10:09:21 +0100 Subject: [PATCH 11/18] [#188] Prepare Nix shell configuration for frontend and backend Developed Nix shell configurations for both frontend and backend modules to streamline the build and deployment processes and ensure alignment with backend setup and dependencies. The new shell configurations provide necessary tools and dependencies for development environments, optimizing the overall development workflow. Changes: - Updated govtool/backend/default.nix to simplify package retrieval using `` and removed unnecessary sources file inclusion. - Created govtool/backend/shell.nix to define additional dependencies like awscli, docker, git, and gnumake for the backend module. - Added govtool/frontend/shell.nix to specify frontend module dependencies, ensuring consistency with the backend setup. --- govtool/backend/default.nix | 5 +---- govtool/backend/shell.nix | 16 ++++++++++++++++ govtool/frontend/shell.nix | 12 ++++++++++++ 3 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 govtool/backend/shell.nix create mode 100644 govtool/frontend/shell.nix diff --git a/govtool/backend/default.nix b/govtool/backend/default.nix index 1a7696a4a..aab2212bc 100644 --- a/govtool/backend/default.nix +++ b/govtool/backend/default.nix @@ -1,7 +1,4 @@ -# TODO: Remove the sources file and use the nixpkgs version provided from the -# flakes lock file instead when the flakes feature is present and enabled in the -# root of the project. -{ pkgs ? (import ./sources.nix).pkgs }: +{ pkgs ? import {} }: let # This is the version of the Haskell compiler we reccommend using. ghcPackages = pkgs.haskell.packages.ghc927; diff --git a/govtool/backend/shell.nix b/govtool/backend/shell.nix new file mode 100644 index 000000000..e020a95a2 --- /dev/null +++ b/govtool/backend/shell.nix @@ -0,0 +1,16 @@ +{ 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/frontend/shell.nix b/govtool/frontend/shell.nix new file mode 100644 index 000000000..ec3697994 --- /dev/null +++ b/govtool/frontend/shell.nix @@ -0,0 +1,12 @@ +{ pkgs ? import {} }: +let + project = import ./default.nix { inherit pkgs; }; +in +project.overrideAttrs (attrs: { + buildInputs = attrs.buildInputs ++ (with pkgs; [ + awscli + docker + git + gnumake + ]); +}) From 49b75c0a9e8633c9ba811d9c5c8461f0abe60e9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Sworze=C5=84?= Date: Tue, 12 Mar 2024 10:07:49 +0100 Subject: [PATCH 12/18] fix copy for cancel governance action creation --- govtool/frontend/src/i18n/locales/en.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/govtool/frontend/src/i18n/locales/en.ts b/govtool/frontend/src/i18n/locales/en.ts index 6d08237c9..a01d9de24 100644 --- a/govtool/frontend/src/i18n/locales/en.ts +++ b/govtool/frontend/src/i18n/locales/en.ts @@ -328,8 +328,9 @@ export const en = { }, createGovernanceAction: { cancelModalDescription: - "If you return to the Dashboard, your information will not be saved.", - cancelModalTitle: "Do You Want to Cancel Registration ?", + "Returning to the Dashboard will cancel your submission and your proposed Governance Action will not be submitted.", + cancelModalTitle: + "Do you want to Cancel your Governance Action submission?", }, delegation: { message: From 2de724bd188c851de86b9eff03ad9f0ce0a48999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sza=C5=82owski?= Date: Mon, 11 Mar 2024 20:22:58 +0100 Subject: [PATCH 13/18] [#377, #432] feat: form inputs validation --- CHANGELOG.md | 17 ++- .../src/components/atoms/TextArea.tsx | 1 + .../components/molecules/Field/TextArea.tsx | 13 +- .../ChooseGovernanceActionType.tsx | 40 +++--- .../CreateGovernanceActionForm.tsx | 101 ++++++++------- .../ReviewCreatedGovernanceAction.tsx | 4 +- .../StorageInformation.tsx | 27 +++- .../StoreDataInfo.tsx | 8 +- .../WhatGovernanceActionIsAbout.tsx | 6 +- .../CreateGovernanceActionSteps/index.ts | 4 + .../src/components/organisms/index.ts | 4 - .../src/consts/governanceActionFields.ts | 122 ++++++++++++++++++ .../src/consts/governanceActionTypes.ts | 64 --------- govtool/frontend/src/consts/index.ts | 2 +- .../{index.tsx => contextProviders.tsx} | 0 govtool/frontend/src/context/index.ts | 4 + .../forms/useCreateGovernanceActionForm.ts | 24 ++-- govtool/frontend/src/i18n/locales/en.ts | 38 ++++++ .../src/pages/CreateGovernanceAction.tsx | 4 +- govtool/frontend/src/types/global.d.ts | 8 ++ .../frontend/src/types/governanceAction.ts | 46 +++++++ 21 files changed, 367 insertions(+), 170 deletions(-) rename govtool/frontend/src/components/organisms/{ => CreateGovernanceActionSteps}/ChooseGovernanceActionType.tsx (65%) rename govtool/frontend/src/components/organisms/{ => CreateGovernanceActionSteps}/CreateGovernanceActionForm.tsx (61%) rename govtool/frontend/src/components/organisms/{ => CreateGovernanceActionSteps}/ReviewCreatedGovernanceAction.tsx (98%) rename govtool/frontend/src/components/organisms/{ => CreateGovernanceActionSteps}/WhatGovernanceActionIsAbout.tsx (94%) create mode 100644 govtool/frontend/src/consts/governanceActionFields.ts delete mode 100644 govtool/frontend/src/consts/governanceActionTypes.ts rename govtool/frontend/src/context/{index.tsx => contextProviders.tsx} (100%) create mode 100644 govtool/frontend/src/context/index.ts create mode 100644 govtool/frontend/src/types/governanceAction.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 059c7167d..1b4be57e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,23 +9,27 @@ As a minor extension, we also keep a semantic version for the `UNRELEASED` changes. ## [Unreleased] + - Create GA review subbmision page [Issue 362](https://github.com/IntersectMBO/govtool/issues/362) - Create GA creation form [Issue 360](https://github.com/IntersectMBO/govtool/issues/360) - Create TextArea [Issue 110](https://github.com/IntersectMBO/govtool/issues/110) +- Choose GA type - GA Submiter [Issue 358](https://github.com/IntersectMBO/govtool/issues/358) + +- Add on-chain inputs validation [Issue 377](https://github.com/IntersectMBO/govtool/issues/377) + +### Added + +- Added `isRegisteredAsSoleVoter` and `wasRegisteredAsSoleVoter` fields to the drep/info response [Issue 212](https://github.com/IntersectMBO/govtool/issues/212) - Abandoning registration as DRep [Issue 151](https://github.com/IntersectMBO/govtool/issues/151) - Abandoning GA creation [Issue 359](https://github.com/IntersectMBO/govtool/issues/359) -- Choose GA type - GA Submiter [Issue 358](https://github.com/IntersectMBO/govtool/issues/358) -- Change step 3 components [Issue 152](https://github.com/intersectMBO/govtool/issues/152) -- Add possibility to vote on behalf of myself - Sole Voter [Issue 119](https://github.com/IntersectMBO/govtool/issues/119) - Create DRep registration page about roles [Issue 205](https://github.com/IntersectMBO/govtool/issues/205) - Create Checkbox component. Improve Field and ControlledField [Issue 177](https://github.com/IntersectMBO/govtool/pull/177) - 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) - -### Added -- Added `isRegisteredAsSoleVoter` and `wasRegisteredAsSoleVoter` fields to the drep/info response [Issue 212](https://github.com/IntersectMBO/govtool/issues/212) +- Add possibility to vote on behalf of myself - Sole Voter [Issue 119](https://github.com/IntersectMBO/govtool/issues/119) ### Fixed + - Fix drep type detection when changing metadata [Issue 333](https://github.com/IntersectMBO/govtool/issues/333) - Fix make button disble when wallet tries connect [Issue 265](https://github.com/IntersectMBO/govtool/issues/265) - Fix drep voting power calculation [Issue 231](https://github.com/IntersectMBO/govtool/issues/231) @@ -58,6 +62,7 @@ changes. - Added a grafana panel to track all the deploys on the target machines [Issue 361](https://github.com/IntersectMBO/govtool/issues/361). ### Removed + - ## [sancho-v1.0.0](https://github.com/IntersectMBO/govtool/releases/tag/sancho-v1.0.0) 2023-12-17 diff --git a/govtool/frontend/src/components/atoms/TextArea.tsx b/govtool/frontend/src/components/atoms/TextArea.tsx index 06c778a04..0e5b0eacd 100644 --- a/govtool/frontend/src/components/atoms/TextArea.tsx +++ b/govtool/frontend/src/components/atoms/TextArea.tsx @@ -55,6 +55,7 @@ export const TextArea = forwardRef( ( {label} )} - + ( /> {props?.value?.toString()?.length ?? 0}/{maxLength} diff --git a/govtool/frontend/src/components/organisms/ChooseGovernanceActionType.tsx b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/ChooseGovernanceActionType.tsx similarity index 65% rename from govtool/frontend/src/components/organisms/ChooseGovernanceActionType.tsx rename to govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/ChooseGovernanceActionType.tsx index da64375e6..e90606635 100644 --- a/govtool/frontend/src/components/organisms/ChooseGovernanceActionType.tsx +++ b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/ChooseGovernanceActionType.tsx @@ -1,14 +1,13 @@ import { Dispatch, SetStateAction } from "react"; - import { ActionRadio, Spacer, Typography } from "@atoms"; -import { GOVERNANCE_ACTION_TYPES } from "@consts"; import { useCreateGovernanceActionForm, useScreenDimension, useTranslation, } from "@hooks"; +import { GovernanceActionType } from "@/types/governanceAction"; -import { BgCard } from "./BgCard"; +import { BgCard } from "../BgCard"; type ChooseGovernanceActionTypeProps = { setStep: Dispatch>; @@ -32,25 +31,26 @@ export const ChooseGovernanceActionType = ({ }; // TODO: Add tooltips when they will be available - const renderGovernanceActionTypes = () => { - return GOVERNANCE_ACTION_TYPES.map((type, index) => { - const isChecked = getValues("governance_action_type") === type; - return ( -
- - {index + 1 < GOVERNANCE_ACTION_TYPES.length ? : null} -
- ); - }); - }; + const renderGovernanceActionTypes = () => + Object.keys(GovernanceActionType).map( + (type, index, governanceActionTypes) => { + const isChecked = getValues("governance_action_type") === type; + return ( +
+ + {index + 1 < governanceActionTypes.length ? : null} +
+ ); + } + ); const onChangeType = (value: string) => { - setValue("governance_action_type", value); + setValue("governance_action_type", value as GovernanceActionType); }; return ( diff --git a/govtool/frontend/src/components/organisms/CreateGovernanceActionForm.tsx b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/CreateGovernanceActionForm.tsx similarity index 61% rename from govtool/frontend/src/components/organisms/CreateGovernanceActionForm.tsx rename to govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/CreateGovernanceActionForm.tsx index b48a536af..0d49018e5 100644 --- a/govtool/frontend/src/components/organisms/CreateGovernanceActionForm.tsx +++ b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/CreateGovernanceActionForm.tsx @@ -3,26 +3,32 @@ import { useFieldArray } from "react-hook-form"; import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline"; import { Button, InfoText, Spacer, Typography } from "@atoms"; -import { GOVERNANCE_ACTIONS_FIELDS } from "@consts"; +import { GOVERNANCE_ACTION_FIELDS } from "@consts"; import { useCreateGovernanceActionForm, useTranslation } from "@hooks"; import { Field } from "@molecules"; -import { BgCard } from "./BgCard"; -import { ControlledField } from "./ControlledField"; +import { BgCard } from "../BgCard"; +import { ControlledField } from "../ControlledField"; +import { GovernanceActionField } from "@/types/governanceAction"; +import { URL_REGEX } from "@/utils"; const LINK_PLACEHOLDER = "https://website.com/"; const MAX_NUMBER_OF_LINKS = 8; -type ChooseGovernanceActionTypeProps = { +type CreateGovernanceActionFormProps = { setStep: Dispatch>; }; export const CreateGovernanceActionForm = ({ setStep, -}: ChooseGovernanceActionTypeProps) => { +}: CreateGovernanceActionFormProps) => { const { t } = useTranslation(); const { control, errors, getValues, register, reset, watch } = useCreateGovernanceActionForm(); + + const isError = Object.keys(errors).length > 0; + + const type = getValues("governance_action_type"); const { append, fields: links, @@ -32,16 +38,11 @@ export const CreateGovernanceActionForm = ({ name: "links", }); - const governanceActionType = getValues("governance_action_type"); - const fields = - GOVERNANCE_ACTIONS_FIELDS.find( - (field) => field.name === governanceActionType - )?.fields ?? []; - // TODO: Replace any - const isContinueButtonDisabled = Object.keys(fields).some( - (field: any) => !watch(field) - ); + const isContinueButtonDisabled = + Object.keys(GOVERNANCE_ACTION_FIELDS[type!]).some( + (field: any) => !watch(field) + ) || isError; const onClickContinue = () => { setStep(4); @@ -53,37 +54,35 @@ export const CreateGovernanceActionForm = ({ }; const renderGovernanceActionField = () => { - return Object.entries(fields).map(([key, value]) => { - const label = - key.charAt(0).toUpperCase() + key.slice(1).replace("_", " "); - - if (value.component === "input") { - return ( - - ); - } - if (value.component === "textarea") { - return ( - - ); + return Object.entries(GOVERNANCE_ACTION_FIELDS[type!]).map( + ([key, field]) => { + const fieldProps = { + helpfulText: field.tipI18nKey ? t(field.tipI18nKey) : undefined, + key, + label: t(field.labelI18nKey), + layoutStyles: { mb: 3 }, + name: key, + placeholder: field.placeholderI18nKey + ? t(field.placeholderI18nKey) + : undefined, + rules: field.rules, + }; + + if (field.component === GovernanceActionField.Input) { + return ( + + ); + } + if (field.component === GovernanceActionField.TextArea) { + return ( + + ); + } } - }); + ); }; const addLink = useCallback(() => { @@ -102,6 +101,7 @@ export const CreateGovernanceActionForm = ({ return ( 1 ? ( ); }); @@ -136,7 +147,7 @@ export const CreateGovernanceActionForm = ({ disabled={true} helpfulText={t("forms.createGovernanceAction.typeTip")} label={t("forms.createGovernanceAction.typeLabel")} - value={governanceActionType} + value={type} /> {renderGovernanceActionField()} diff --git a/govtool/frontend/src/components/organisms/ReviewCreatedGovernanceAction.tsx b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/ReviewCreatedGovernanceAction.tsx similarity index 98% rename from govtool/frontend/src/components/organisms/ReviewCreatedGovernanceAction.tsx rename to govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/ReviewCreatedGovernanceAction.tsx index f05db801e..6afc22249 100644 --- a/govtool/frontend/src/components/organisms/ReviewCreatedGovernanceAction.tsx +++ b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/ReviewCreatedGovernanceAction.tsx @@ -1,4 +1,3 @@ -import { Dispatch, SetStateAction } from "react"; import { Box } from "@mui/material"; import DriveFileRenameOutlineOutlinedIcon from "@mui/icons-material/DriveFileRenameOutlineOutlined"; @@ -12,7 +11,8 @@ import { import { LinkWithIcon } from "@molecules"; import { openInNewTab } from "@utils"; -import { BgCard } from "./BgCard"; +import { BgCard } from "../BgCard"; +import { Dispatch, SetStateAction } from "react"; type ReviewCreatedGovernanceActionProps = { setStep: Dispatch>; diff --git a/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StorageInformation.tsx b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StorageInformation.tsx index 38e9dbbcf..41bd01112 100644 --- a/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StorageInformation.tsx +++ b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StorageInformation.tsx @@ -6,13 +6,13 @@ import { Button, Spacer, Typography } from "@atoms"; import { useCreateGovernanceActionForm, useTranslation } from "@hooks"; import { Step } from "@molecules"; import { BgCard, ControlledField } from "@organisms"; -import { downloadJson, openInNewTab } from "@utils"; +import { URL_REGEX, downloadJson, openInNewTab } from "@utils"; -export const StorageInformation = ({ - setStep, -}: { +type StorageInformationProps = { setStep: Dispatch>; -}) => { +}; + +export const StorageInformation = ({ setStep }: StorageInformationProps) => { const { t } = useTranslation(); const { control, @@ -23,6 +23,7 @@ export const StorageInformation = ({ watch, } = useCreateGovernanceActionForm(); const [isJsonDownloaded, setIsJsonDownloaded] = useState(false); + // TODO: change on correct file name const fileName = getValues("governance_action_type"); @@ -36,7 +37,7 @@ export const StorageInformation = ({ const onClickBack = useCallback(() => setStep(5), []); - const onClickDowloadJson = () => { + const onClickDownloadJson = () => { const data = getValues(); const jsonBody = generateJsonBody(data); downloadJson(jsonBody, fileName); @@ -66,7 +67,7 @@ export const StorageInformation = ({ // TODO: add onClick action when available component={