structure migration (Next.js)

This commit is contained in:
jejolare 2024-09-18 16:51:45 +07:00
parent 4c50c7447c
commit e78a515307
62 changed files with 779 additions and 521 deletions

1
.gitignore vendored
View file

@ -18,6 +18,7 @@ node_modules
.env.test.local .env.test.local
.env.production.local .env.production.local
.env .env
.next
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*

5
next-env.d.ts vendored Normal file
View file

@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.

29
next.config.js Normal file
View file

@ -0,0 +1,29 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
webpack: (config) => {
const fileLoaderRule = config.module.rules.find((rule) =>
rule.test?.test?.(".svg")
);
config.module.rules.push(
// Reapply the existing rule, but only for svg imports ending in ?url
{
...fileLoaderRule,
test: /\.svg$/i,
resourceQuery: /url/ // *.svg?url
},
// Convert all other *.svg imports to React components
{
test: /\.svg$/i,
issuer: fileLoaderRule.issuer,
resourceQuery: { not: [...fileLoaderRule.resourceQuery.not, /url/] }, // exclude if *.svg?url
use: ["@svgr/webpack"]
}
);
return config;
},
}
export default nextConfig;

321
package-lock.json generated
View file

@ -23,10 +23,10 @@
"highcharts-react-official": "^3.2.1", "highcharts-react-official": "^3.2.1",
"http-proxy-middleware": "^2.0.6", "http-proxy-middleware": "^2.0.6",
"nanoid": "^5.0.1", "nanoid": "^5.0.1",
"react": "^18.2.0", "next": "^14.2.12",
"react-dom": "^18.2.0", "react": "^18.3.1",
"react-dom": "^18.3.1",
"react-json-view-lite": "^1.1.0", "react-json-view-lite": "^1.1.0",
"react-router-dom": "^6.16.0",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"sass": "^1.68.0", "sass": "^1.68.0",
"socket.io-client": "^4.7.5", "socket.io-client": "^4.7.5",
@ -3519,6 +3519,146 @@
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
"integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A=="
}, },
"node_modules/@next/env": {
"version": "14.2.12",
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.12.tgz",
"integrity": "sha512-3fP29GIetdwVIfIRyLKM7KrvJaqepv+6pVodEbx0P5CaMLYBtx+7eEg8JYO5L9sveJO87z9eCReceZLi0hxO1Q=="
},
"node_modules/@next/swc-darwin-arm64": {
"version": "14.2.12",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.12.tgz",
"integrity": "sha512-crHJ9UoinXeFbHYNok6VZqjKnd8rTd7K3Z2zpyzF1ch7vVNKmhjv/V7EHxep3ILoN8JB9AdRn/EtVVyG9AkCXw==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-darwin-x64": {
"version": "14.2.12",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.12.tgz",
"integrity": "sha512-JbEaGbWq18BuNBO+lCtKfxl563Uw9oy2TodnN2ioX00u7V1uzrsSUcg3Ep9ce+P0Z9es+JmsvL2/rLphz+Frcw==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-linux-arm64-gnu": {
"version": "14.2.12",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.12.tgz",
"integrity": "sha512-qBy7OiXOqZrdp88QEl2H4fWalMGnSCrr1agT/AVDndlyw2YJQA89f3ttR/AkEIP9EkBXXeGl6cC72/EZT5r6rw==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-linux-arm64-musl": {
"version": "14.2.12",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.12.tgz",
"integrity": "sha512-EfD9L7o9biaQxjwP1uWXnk3vYZi64NVcKUN83hpVkKocB7ogJfyH2r7o1pPnMtir6gHZiGCeHKagJ0yrNSLNHw==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-linux-x64-gnu": {
"version": "14.2.12",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.12.tgz",
"integrity": "sha512-iQ+n2pxklJew9IpE47hE/VgjmljlHqtcD5UhZVeHICTPbLyrgPehaKf2wLRNjYH75udroBNCgrSSVSVpAbNoYw==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-linux-x64-musl": {
"version": "14.2.12",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.12.tgz",
"integrity": "sha512-rFkUkNwcQ0ODn7cxvcVdpHlcOpYxMeyMfkJuzaT74xjAa5v4fxP4xDk5OoYmPi8QNLDs3UgZPMSBmpBuv9zKWA==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-win32-arm64-msvc": {
"version": "14.2.12",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.12.tgz",
"integrity": "sha512-PQFYUvwtHs/u0K85SG4sAdDXYIPXpETf9mcEjWc0R4JmjgMKSDwIU/qfZdavtP6MPNiMjuKGXHCtyhR/M5zo8g==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-win32-ia32-msvc": {
"version": "14.2.12",
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.12.tgz",
"integrity": "sha512-FAj2hMlcbeCV546eU2tEv41dcJb4NeqFlSXU/xL/0ehXywHnNpaYajOUvn3P8wru5WyQe6cTZ8fvckj/2XN4Vw==",
"cpu": [
"ia32"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-win32-x64-msvc": {
"version": "14.2.12",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.12.tgz",
"integrity": "sha512-yu8QvV53sBzoIVRHsxCHqeuS8jYq6Lrmdh0briivuh+Brsp6xjg80MAozUsBTAV9KNmY08KlX0KYTWz1lbPzEg==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
"version": "5.1.1-v1", "version": "5.1.1-v1",
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
@ -3628,14 +3768,6 @@
} }
} }
}, },
"node_modules/@remix-run/router": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.9.0.tgz",
"integrity": "sha512-bV63itrKBC0zdT27qYm6SDZHlkXwFL1xMBuhkn+X7l0+IIhNaH5wuuvZKp6eKhCD4KFhujhfhCT1YxXW6esUIA==",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@rollup/plugin-babel": { "node_modules/@rollup/plugin-babel": {
"version": "5.3.1", "version": "5.3.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
@ -3959,6 +4091,20 @@
"url": "https://github.com/sponsors/gregberge" "url": "https://github.com/sponsors/gregberge"
} }
}, },
"node_modules/@swc/counter": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
"integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="
},
"node_modules/@swc/helpers": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz",
"integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==",
"dependencies": {
"@swc/counter": "^0.1.3",
"tslib": "^2.4.0"
}
},
"node_modules/@testing-library/dom": { "node_modules/@testing-library/dom": {
"version": "9.3.3", "version": "9.3.3",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.3.tgz", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.3.tgz",
@ -5958,6 +6104,17 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
"dependencies": {
"streamsearch": "^1.1.0"
},
"engines": {
"node": ">=10.16.0"
}
},
"node_modules/bytes": { "node_modules/bytes": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
@ -6026,9 +6183,9 @@
} }
}, },
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001546", "version": "1.0.30001660",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001546.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz",
"integrity": "sha512-zvtSJwuQFpewSyRrI3AsftF6rM0X80mZkChIt1spBGEvRglCrjTniXvinc8JKRoqTwXAgvqTImaN9igfSMtUBw==", "integrity": "sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@ -6161,6 +6318,11 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/client-only": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="
},
"node_modules/cliui": { "node_modules/cliui": {
"version": "7.0.4", "version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
@ -12940,6 +13102,55 @@
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
}, },
"node_modules/next": {
"version": "14.2.12",
"resolved": "https://registry.npmjs.org/next/-/next-14.2.12.tgz",
"integrity": "sha512-cDOtUSIeoOvt1skKNihdExWMTybx3exnvbFbb9ecZDIxlvIbREQzt9A5Km3Zn3PfU+IFjyYGsHS+lN9VInAGKA==",
"dependencies": {
"@next/env": "14.2.12",
"@swc/helpers": "0.5.5",
"busboy": "1.6.0",
"caniuse-lite": "^1.0.30001579",
"graceful-fs": "^4.2.11",
"postcss": "8.4.31",
"styled-jsx": "5.1.1"
},
"bin": {
"next": "dist/bin/next"
},
"engines": {
"node": ">=18.17.0"
},
"optionalDependencies": {
"@next/swc-darwin-arm64": "14.2.12",
"@next/swc-darwin-x64": "14.2.12",
"@next/swc-linux-arm64-gnu": "14.2.12",
"@next/swc-linux-arm64-musl": "14.2.12",
"@next/swc-linux-x64-gnu": "14.2.12",
"@next/swc-linux-x64-musl": "14.2.12",
"@next/swc-win32-arm64-msvc": "14.2.12",
"@next/swc-win32-ia32-msvc": "14.2.12",
"@next/swc-win32-x64-msvc": "14.2.12"
},
"peerDependencies": {
"@opentelemetry/api": "^1.1.0",
"@playwright/test": "^1.41.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"sass": "^1.3.0"
},
"peerDependenciesMeta": {
"@opentelemetry/api": {
"optional": true
},
"@playwright/test": {
"optional": true
},
"sass": {
"optional": true
}
}
},
"node_modules/no-case": { "node_modules/no-case": {
"version": "3.0.4", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
@ -15045,9 +15256,9 @@
} }
}, },
"node_modules/react": { "node_modules/react": {
"version": "18.2.0", "version": "18.3.1",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
"dependencies": { "dependencies": {
"loose-envify": "^1.1.0" "loose-envify": "^1.1.0"
}, },
@ -15194,15 +15405,15 @@
} }
}, },
"node_modules/react-dom": { "node_modules/react-dom": {
"version": "18.2.0", "version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
"dependencies": { "dependencies": {
"loose-envify": "^1.1.0", "loose-envify": "^1.1.0",
"scheduler": "^0.23.0" "scheduler": "^0.23.2"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^18.2.0" "react": "^18.3.1"
} }
}, },
"node_modules/react-error-overlay": { "node_modules/react-error-overlay": {
@ -15234,36 +15445,6 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/react-router": {
"version": "6.16.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.16.0.tgz",
"integrity": "sha512-VT4Mmc4jj5YyjpOi5jOf0I+TYzGpvzERy4ckNSvSh2RArv8LLoCxlsZ2D+tc7zgjxcY34oTz2hZaeX5RVprKqA==",
"dependencies": {
"@remix-run/router": "1.9.0"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"react": ">=16.8"
}
},
"node_modules/react-router-dom": {
"version": "6.16.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.16.0.tgz",
"integrity": "sha512-aTfBLv3mk/gaKLxgRDUPbPw+s4Y/O+ma3rEN1u8EgEpLpPe6gNjIsWt9rxushMHHMb7mSwxRGdGlGdvmFsyPIg==",
"dependencies": {
"@remix-run/router": "1.9.0",
"react-router": "6.16.0"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"react": ">=16.8",
"react-dom": ">=16.8"
}
},
"node_modules/react-scripts": { "node_modules/react-scripts": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz",
@ -15915,9 +16096,9 @@
} }
}, },
"node_modules/scheduler": { "node_modules/scheduler": {
"version": "0.23.0", "version": "0.23.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
"integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
"dependencies": { "dependencies": {
"loose-envify": "^1.1.0" "loose-envify": "^1.1.0"
} }
@ -16486,6 +16667,14 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/streamsearch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/string_decoder": { "node_modules/string_decoder": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
@ -16675,6 +16864,28 @@
"webpack": "^5.0.0" "webpack": "^5.0.0"
} }
}, },
"node_modules/styled-jsx": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz",
"integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==",
"dependencies": {
"client-only": "0.0.1"
},
"engines": {
"node": ">= 12.0.0"
},
"peerDependencies": {
"react": ">= 16.8.0 || 17.x.x || ^18.0.0-0"
},
"peerDependenciesMeta": {
"@babel/core": {
"optional": true
},
"babel-plugin-macros": {
"optional": true
}
}
},
"node_modules/stylehacks": { "node_modules/stylehacks": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz",

View file

@ -2,6 +2,7 @@
"name": "zano-explorer", "name": "zano-explorer",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"type": "module",
"dependencies": { "dependencies": {
"@testing-library/jest-dom": "^5.17.0", "@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
@ -17,10 +18,10 @@
"highcharts-react-official": "^3.2.1", "highcharts-react-official": "^3.2.1",
"http-proxy-middleware": "^2.0.6", "http-proxy-middleware": "^2.0.6",
"nanoid": "^5.0.1", "nanoid": "^5.0.1",
"react": "^18.2.0", "next": "^14.2.12",
"react-dom": "^18.2.0", "react": "^18.3.1",
"react-dom": "^18.3.1",
"react-json-view-lite": "^1.1.0", "react-json-view-lite": "^1.1.0",
"react-router-dom": "^6.16.0",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"sass": "^1.68.0", "sass": "^1.68.0",
"socket.io-client": "^4.7.5", "socket.io-client": "^4.7.5",
@ -30,11 +31,11 @@
}, },
"scripts": { "scripts": {
"postinstall": "cd server && npm i", "postinstall": "cd server && npm i",
"client": "react-scripts start", "client": "next dev",
"build": "react-scripts build", "build": "react-scripts build",
"test": "react-scripts test", "test": "react-scripts test",
"eject": "react-scripts eject", "eject": "react-scripts eject",
"dev": "concurrently \"npx nodemon --exec tsx ./server/server.ts\" \"npm run client\"", "dev": "npx nodemon --exec tsx ./server/server.ts",
"start": "tsx ./server/server.ts" "start": "tsx ./server/server.ts"
}, },
"eslintConfig": { "eslintConfig": {

View file

@ -22,13 +22,18 @@ import Pool from "./schemes/Pool";
import Asset, { IAsset } from "./schemes/Asset"; import Asset, { IAsset } from "./schemes/Asset";
import { ITransaction } from "./schemes/Transaction"; import { ITransaction } from "./schemes/Transaction";
import BigNumber from "bignumber.js"; import BigNumber from "bignumber.js";
import next from "next";
import { rateLimit } from 'express-rate-limit'; import { rateLimit } from 'express-rate-limit';
// @ts-ignore // @ts-ignore
const __dirname = import.meta.dirname; const __dirname = import.meta.dirname;
const dev = process.env.NODE_ENV !== 'production';
const nextApp = next({ dev });
const handle = nextApp.getRequestHandler();
const app = express(); const app = express();
const server = http.createServer(app); const server = http.createServer(app);
export const io = new Server(server, { transports: ['websocket', 'polling'] }); export const io = new Server(server, { transports: ['websocket', 'polling'] });
@ -40,6 +45,9 @@ const requestsLimiter = rateLimit({
}); });
(async () => { (async () => {
await nextApp.prepare();
await initDB(); await initDB();
await sequelize.authenticate(); await sequelize.authenticate();
await sequelize.sync(); await sequelize.sync();
@ -50,7 +58,11 @@ const requestsLimiter = rateLimit({
io.engine.on('headers', (headers, req) => { io.engine.on('headers', (headers, req) => {
headers['Access-Control-Allow-Origin'] = config.frontend_api headers['Access-Control-Allow-Origin'] = config.frontend_api
}) });
app.all('*', (req, res) => {
return handle(req, res);
});
app.use(express.static('dist')); app.use(express.static('dist'));
app.use(function (req, res, next) { app.use(function (req, res, next) {
@ -1121,9 +1133,7 @@ const requestsLimiter = rateLimit({
let localTr: any; let localTr: any;
while (!!(localTr = bl.transactions_details.splice(0, 1)[0])) { while (!!(localTr = bl.transactions_details.splice(0, 1)[0])) {
console.time('txs details call');
let response = await get_tx_details(localTr.id); let response = await get_tx_details(localTr.id);
console.timeEnd('txs details call');
let tx_info = response.data.result.tx_info; let tx_info = response.data.result.tx_info;

View file

@ -1,42 +0,0 @@
import React, { Suspense } from "react";
import { BrowserRouter as Router, Routes, Route, Navigate } from "react-router-dom";
import Block from "./pages/Block/Block";
import { NET_MODE } from "./config/config";
const Blockchain = React.lazy(() => import("./pages/Blockchain/Blockchain"));
const AltBlocks = React.lazy(() => import("./pages/AltBlocks/AltBlocks"));
const Aliases = React.lazy(() => import("./pages/Aliases/Aliases"));
const API = React.lazy(() => import("./pages/API/API"));
const Transaction = React.lazy(() => import("./pages/Transaction/Transaction"));
const Charts = React.lazy(() => import("./pages/Charts/Charts"));
const ChartsPage = React.lazy(() => import("./pages/ChartPage/ChartPage"));
const Assets = React.lazy(() => import("./pages/Assets/Assets"));
const Asset = React.lazy(() => import("./pages/Asset/Asset"));
function App() {
return (
<Router>
<Suspense fallback={<></>}>
<Routes>
<Route path="/" element={<Blockchain />} />
<Route path="/alt-blocks" element={<AltBlocks />} />
<Route path="/aliases" element={<Aliases />} />
<Route path="/zano_api" element={<API />} />
<Route path="/block/:hash" element={<Block />} />
<Route path="/transaction/:hash" element={<Transaction />} />
<Route path="/alt-blocks/:hash" element={<Block alt />} />
{/* <Route path="/charts" element={<Charts />} />
<Route path="/charts/:name" element={<ChartsPage />} /> */}
{/* {NET_MODE === "TEST" &&
<Route path="/assets" element={<Assets />} />
} */}
<Route path="/assets" element={<Assets />} />
<Route path="/*" element={<Navigate to="/" />} />
{/* <Route path="/assets/:id" element={<Asset />} /> */}
</Routes>
</Suspense>
</Router>
);
}
export default App;

View file

@ -1,4 +1,4 @@
import "./Button.scss"; import styles from "./Button.module.scss";
interface ButtonProps extends React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement> { interface ButtonProps extends React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement> {
wrapper?: boolean wrapper?: boolean
@ -9,7 +9,7 @@ function Button(props: ButtonProps) {
return ( return (
<button <button
className={`button ${wrapper ? "button__wrapper" : ""} ${className || ""}`} className={`${styles.button} ${wrapper ? styles["button__wrapper"] : ""} ${className || ""}`}
{...restProps} {...restProps}
> >
{children} {children}

View file

@ -1,6 +1,6 @@
import "./CopyButton.scss"; import styles from "@/components/UI/CopyButton/CopyButton.module.scss";
import Button from "../UI/Button/Button"; import Button from "@/components/UI/Button/Button";
import { ReactComponent as CopyImg } from "../../assets/images/UI/copy.svg"; import CopyImg from "@/assets/images/UI/copy.svg";
import { useState } from "react"; import { useState } from "react";
@ -18,7 +18,7 @@ export default function CopyButton({ text }: { text: string }) {
} }
return ( return (
<Button className={copied ? "copy-button_copied" : undefined} onClick={onClick} wrapper> <Button className={copied ? styles["copy-button_copied"] : undefined} onClick={onClick} wrapper>
<CopyImg /> <CopyImg />
</Button> </Button>
); );

View file

@ -1,4 +1,4 @@
import "./Input.scss"; import styles from "./Input.module.scss";
interface InputProps extends React.HTMLProps<HTMLInputElement> { interface InputProps extends React.HTMLProps<HTMLInputElement> {
onEnterPress?: () => void; onEnterPress?: () => void;
@ -19,7 +19,7 @@ function Input(props: InputProps) {
<input <input
{...restProps} {...restProps}
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
className={"input " + (props.className || "")} className={`${styles["input"]} ${props.className ? styles[props.className] : ""}`}
/> />
) )
} }

View file

@ -1,4 +1,4 @@
import "./JsonViewStyled.scss"; import styles from "./JsonViewStyled.module.scss";
import { JsonView, darkStyles, Props } from "react-json-view-lite"; import { JsonView, darkStyles, Props } from "react-json-view-lite";
import 'react-json-view-lite/dist/index.css'; import 'react-json-view-lite/dist/index.css';
@ -6,16 +6,14 @@ function JsonViewStyled(props: Props) {
return ( return (
<JsonView <JsonView
{...props} {...props}
style={ style={{
{ ...darkStyles,
...darkStyles, container: styles["json__container"],
container: "json__container", basicChildStyle: styles["json__element"],
basicChildStyle: "json__element", numberValue: styles["json__number"]
numberValue: "json__number" }}
}
}
/> />
) )
} }
export default JsonViewStyled; export default JsonViewStyled;

View file

@ -1,11 +1,16 @@
import "./Preloader.scss"; import styles from "./Preloader.module.scss";
function Preloader(props: { className?: string }) { function Preloader(props: { className?: string }) {
const { className } = props; const { className } = props;
return ( return (
<div className={"lds-ellipsis " + (className || "")}><div></div><div></div><div></div><div></div></div> <div className={styles["lds-ellipsis"] + " " + (className || "")}>
) <div></div>
<div></div>
<div></div>
<div></div>
</div>
);
} }
export default Preloader; export default Preloader;

View file

@ -1,5 +1,5 @@
import Button from "../Button/Button"; import Button from "../Button/Button";
import "./Switch.scss"; import styles from "./Switch.module.scss";
import { Dispatch, SetStateAction } from "react"; import { Dispatch, SetStateAction } from "react";
interface SwitchProps { interface SwitchProps {
@ -16,17 +16,17 @@ export default function Switch({
setIsFirstSelected setIsFirstSelected
}: SwitchProps) { }: SwitchProps) {
return ( return (
<div className="switch"> <div className={styles["switch"]}>
<Button <Button
wrapper wrapper
className={`switch__item ${isFirstSelected ? "switch__item_selected" : ""}`} className={`${styles["switch__item"]} ${isFirstSelected ? styles["switch__item_selected"] : ""}`}
onClick={() => setIsFirstSelected(true)} onClick={() => setIsFirstSelected(true)}
> >
<p>{firstTitle}</p> <p>{firstTitle}</p>
</Button> </Button>
<Button <Button
wrapper wrapper
className={`switch__item ${!isFirstSelected ? "switch__item_selected" : ""}`} className={`${styles["switch__item"]} ${!isFirstSelected ? styles["switch__item_selected"] : ""}`}
onClick={() => setIsFirstSelected(false)} onClick={() => setIsFirstSelected(false)}
> >
<p>{secondTitle}</p> <p>{secondTitle}</p>

View file

@ -1,15 +1,14 @@
import { Link, LinkProps } from "react-router-dom"; import styles from "./AliasText.module.scss";
import "./AliasText.scss"; import Link, { LinkProps } from "next/link";
interface AliasTextProps extends LinkProps { interface AliasTextProps extends LinkProps {
children: React.ReactNode children: React.ReactNode,
} }
function AliasText(props: AliasTextProps) { function AliasText(props: AliasTextProps) {
const { children, ...restProps } = props; const { children, ...restProps } = props;
return ( return (
<span className="alias__text"> <span className={styles["alias__text"]}>
<Link {...restProps}> <Link {...restProps}>
{children} {children}
</Link> </Link>

View file

@ -1,10 +1,10 @@
import "./Header.scss"; import styles from "./Header.module.scss";
import { ReactComponent as LogoImg } from "../../../assets/images/UI/logo.svg"; import LogoImg from "../../../assets/images/UI/logo.svg";
import { ReactComponent as BurgerImg } from "../../../assets/images/UI/burger.svg"; import BurgerImg from "../../../assets/images/UI/burger.svg";
import HeaderProps from "./Header.props"; import HeaderProps from "./Header.props";
import Button from "../../UI/Button/Button"; import Button from "../../UI/Button/Button";
import { NET_MODE } from "../../../config/config"; import { NET_MODE } from "../../../config/config";
import { Link } from "react-router-dom"; import Link from "next/link";
function Header(props: HeaderProps) { function Header(props: HeaderProps) {
const { page, burgerOpened, setBurgerOpened } = props; const { page, burgerOpened, setBurgerOpened } = props;
@ -14,19 +14,19 @@ function Header(props: HeaderProps) {
<nav className={className}> <nav className={className}>
<Link <Link
className={page === "Blockchain" ? "selected" : undefined} className={page === "Blockchain" ? "selected" : undefined}
to="/" href="/"
> >
Blockchain Blockchain
</Link> </Link>
<Link <Link
className={page === "Alt-blocks" ? "selected" : undefined} className={page === "Alt-blocks" ? "selected" : undefined}
to="/alt-blocks" href="/alt-blocks"
> >
Alt-blocks Alt-blocks
</Link> </Link>
<Link <Link
className={page === "Aliases" ? "selected" : undefined} className={page === "Aliases" ? "selected" : undefined}
to="/aliases" href="/aliases"
> >
Aliases Aliases
</Link> </Link>
@ -43,19 +43,19 @@ function Header(props: HeaderProps) {
} */} } */}
<Link <Link
className={page === "Assets" ? "selected" : undefined} className={page === "Assets" ? "selected" : undefined}
to="/assets" href="/assets"
> >
Assets Assets
</Link> </Link>
{/* <Link {/* <Link
className={page === "Charts" ? "selected" : undefined} className={page === "Charts" ? "selected" : undefined}
to="/charts" href="/charts"
> >
Charts Charts
</Link> */} </Link> */}
<Link <Link
className={page === "API" ? "selected" : undefined} className={page === "API" ? "selected" : undefined}
to="/zano_api" href="/zano_api"
> >
API API
</Link> </Link>
@ -69,11 +69,11 @@ function Header(props: HeaderProps) {
} }
return ( return (
<header className="header"> <header className={styles["header"]}>
<div className="header__top"> <div className={styles["header__top"]}>
<div className="header__top__main"> <div className={styles["header__top__main"]}>
<Link to="/"> <Link href="/">
<div className="header__logo"> <div className={styles["header__logo"]}>
<LogoImg /> <LogoImg />
<p>ZANO</p> <p>ZANO</p>
</div> </div>
@ -81,9 +81,9 @@ function Header(props: HeaderProps) {
<Nav /> <Nav />
</div> </div>
<div className="header__top__right"> <div className={styles["header__top__right"]}>
<Link <Link
to={NET_MODE === "TEST" ? "https://explorer.zano.org/" : "https://testnet-explorer.zano.org/"} href={NET_MODE === "TEST" ? "https://explorer.zano.org/" : "https://testnet-explorer.zano.org/"}
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
> >
@ -93,13 +93,13 @@ function Header(props: HeaderProps) {
</Link> </Link>
<Button <Button
onClick={() => setBurgerOpened(!burgerOpened)} onClick={() => setBurgerOpened(!burgerOpened)}
className="header__burger__button" className={styles["header__burger__button"]}
> >
<BurgerImg /> <BurgerImg />
</Button> </Button>
</div> </div>
</div> </div>
{burgerOpened && <Nav className="header__nav__mobile" />} {burgerOpened && <Nav className={styles["header__nav__mobile"]} />}
</header> </header>
) )
} }

View file

@ -1,17 +1,18 @@
import Button from "../../UI/Button/Button"; import Button from "@/components/UI/Button/Button";
import Input from "../../UI/Input/Input"; import Input from "@/components/UI/Input/Input";
import "./InfoTopPanel.scss"; import styles from "./InfoTopPanel.module.scss";
import { ReactComponent as SearchImg } from "../../../assets/images/UI/search.svg"; import SearchImg from "@/assets/images/UI/search.svg";
import { ReactComponent as BackImg } from "../../../assets/images/UI/back.svg"; import BackImg from "@/assets/images/UI/back.svg";
import { useState } from "react"; import { useState } from "react";
import Fetch from "../../../utils/methods"; import Fetch from "@/utils/methods";
import { Link, useNavigate } from "react-router-dom"; import Link from "next/link";
import InfoTopPanelProps from "./InfoTopPanel.props"; import InfoTopPanelProps from "./InfoTopPanel.props";
import { useRouter } from 'next/router'
function InfoTopPanel(props: InfoTopPanelProps) { function InfoTopPanel(props: InfoTopPanelProps) {
const { burgerOpened, title, content, back, className, inputParams, contentNotHiding, inputDefaultClosed } = props; const { burgerOpened, title, content, back, className, inputParams, contentNotHiding, inputDefaultClosed } = props;
const navigate = useNavigate(); const router = useRouter();
const [inputClosed, setInputClosed] = useState(inputDefaultClosed || false); const [inputClosed, setInputClosed] = useState(inputDefaultClosed || false);
const [inputState, setInputState] = useState(""); const [inputState, setInputState] = useState("");
@ -27,23 +28,23 @@ function InfoTopPanel(props: InfoTopPanelProps) {
if (searchInfo && typeof searchInfo === "object") { if (searchInfo && typeof searchInfo === "object") {
const result = searchInfo.result; const result = searchInfo.result;
if (result === "tx") { if (result === "tx") {
return navigate("/transaction/" + input); return router.push("/transaction/" + input);
} }
if (result === "block") { if (result === "block") {
return navigate("/block/" + input); return router.push("/block/" + input);
} }
const txByKeyimageRes = await Fetch.getTxByKeyimage(input); const txByKeyimageRes = await Fetch.getTxByKeyimage(input);
if (txByKeyimageRes && typeof txByKeyimageRes === "object" && txByKeyimageRes.data) { if (txByKeyimageRes && typeof txByKeyimageRes === "object" && txByKeyimageRes.data) {
return navigate("/transaction/" + input); return router.push("/transaction/" + input);
} }
if (input.match(/^\d+$/)) { if (input.match(/^\d+$/)) {
try { try {
const parsedHash = await Fetch.getHashByHeight(parseInt(input, 10)); const parsedHash = await Fetch.getHashByHeight(parseInt(input, 10));
if (parsedHash) { if (parsedHash) {
return navigate("/block/" + parsedHash); return router.push("/block/" + parsedHash);
} }
} catch { } catch {
return setNoMatch(true); return setNoMatch(true);
@ -57,33 +58,30 @@ function InfoTopPanel(props: InfoTopPanelProps) {
async function onBackClick(event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) { async function onBackClick(event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) {
event.preventDefault(); event.preventDefault();
if (document.referrer) { if (document.referrer) {
navigate(-1); router.back();
} else { } else {
navigate("/"); router.push("/");
} }
} }
return ( return (
<div <div
className={ className={
"blockchain__info__top" + " " + className + " " `${styles.blockchain__info__top} ${className} ${inputClosed ? styles.blockchain__input__closed : ""} ${burgerOpened ? styles.info__top__hidden : ""}`
+ (inputClosed ? "blockchain__input__closed" : "") + " "
+ (burgerOpened ? "info__top__hidden" : "")
} }
> >
{back && {back &&
<Link to="/" onClick={onBackClick}> <Link href="/" onClick={onBackClick}>
<div className="info__back"> <div className={styles.info__back}>
<BackImg /> <BackImg />
<p>Back</p> <p>Back</p>
</div> </div>
</Link> </Link>
} }
<div className={"info__top__title"}> <div className={styles.info__top__title}>
<h4 className={contentNotHiding ? "hiding_element" : undefined}>{title}</h4> <h4 className={contentNotHiding ? styles.hiding_element : undefined}>{title}</h4>
{!back && {!back &&
<div className={"info__top__content " + (!contentNotHiding ? "hiding_element" : undefined)}> <div className={`${styles.info__top__content} ${!contentNotHiding ? styles.hiding_element : ""}`}>
{ {
content || <></> content || <></>
} }
@ -93,7 +91,7 @@ function InfoTopPanel(props: InfoTopPanelProps) {
<div className="info__top__input"> <div className={styles.info__top__input}>
{noMatch && <p>No matching records found!</p> } {noMatch && <p>No matching records found!</p> }
{!inputParams ? {!inputParams ?
<Input <Input

View file

@ -1,9 +1,9 @@
import "./JSONPopup.scss"; import styles from "./JSONPopup.module.scss";
import Popup from "../Popup/Popup"; import Popup from "../Popup/Popup";
import JSONPopupProps from "./JSONPopup.props"; import JSONPopupProps from "./JSONPopup.props";
import JsonViewStyled from "../../UI/JsonViewStyled/JsonViewStyled"; import JsonViewStyled from "../../UI/JsonViewStyled/JsonViewStyled";
import Button from "../../UI/Button/Button"; import Button from "../../UI/Button/Button";
import { ReactComponent as CrossImg } from "../../../assets/images/UI/cross.svg"; import CrossImg from "../../../assets/images/UI/cross.svg";
const JSONPopup = (props: JSONPopupProps) => { const JSONPopup = (props: JSONPopupProps) => {
const { const {
@ -23,9 +23,9 @@ const JSONPopup = (props: JSONPopupProps) => {
function PopupContent({ close }: { close: () => void }) { function PopupContent({ close }: { close: () => void }) {
return ( return (
<div className="json_popup__content"> <div className={styles["json_popup__content"]}>
{!hideJson && {!hideJson &&
<div className="json_popup__content__json"> <div className={styles["json_popup__content__json"]}>
<JsonViewStyled <JsonViewStyled
data={json} data={json}
/> />
@ -34,7 +34,7 @@ const JSONPopup = (props: JSONPopupProps) => {
<Button <Button
wrapper wrapper
onClick={close} onClick={close}
className="json_popup__close_btn" className={styles["json_popup__close_btn"]}
> >
<CrossImg /> <CrossImg />
</Button> </Button>

View file

@ -1,12 +1,11 @@
import { NET_MODE } from "../../../config/config"; import { NET_MODE } from "../../../config/config";
import Info from "../../../interfaces/state/Info"; import Info from "../../../interfaces/state/Info";
import VisibilityInfo from "../../../interfaces/state/VisibilityInfo"; import VisibilityInfo from "../../../interfaces/state/VisibilityInfo";
import Fetch from "../../../utils/methods";
import Utils from "../../../utils/utils"; import Utils from "../../../utils/utils";
import "./StatsPanel.scss"; import styles from "./StatsPanel.module.scss";
import { useState, useEffect, ReactNode } from "react"; import { useState, useEffect, ReactNode } from "react";
import { socket } from "../../../utils/socket"; import { socket } from "../../../utils/socket";
import { ReactComponent as BurnImg } from "../../../assets/images/UI/flame_ico.svg"; import BurnImg from "../../../assets/images/UI/flame_ico.svg";
function StatsPanel(props: { visibilityInfo?: VisibilityInfo | null, noStats?: boolean }) { function StatsPanel(props: { visibilityInfo?: VisibilityInfo | null, noStats?: boolean }) {
const { visibilityInfo } = props; const { visibilityInfo } = props;
@ -30,7 +29,6 @@ function StatsPanel(props: { visibilityInfo?: VisibilityInfo | null, noStats?: b
}; };
}, []); }, []);
const transactions = info ? info.height + info.tx_count : undefined; const transactions = info ? info.height + info.tx_count : undefined;
const infoHeight = Utils.formatNumber(info?.height, 0) || "..."; const infoHeight = Utils.formatNumber(info?.height, 0) || "...";
@ -55,23 +53,23 @@ function StatsPanel(props: { visibilityInfo?: VisibilityInfo | null, noStats?: b
const isAmountString = typeof amount === "string"; const isAmountString = typeof amount === "string";
return ( return (
<div className="main__top__item"> <div className={styles["main__top__item"]}>
<div> <div>
<p className="main__top__item__header">{title}</p> <p className={styles["main__top__item__header"]}>{title}</p>
</div> </div>
<div className="item__value"> <div className={styles["item__value"]}>
{isAmountString {isAmountString
? <p>{amount + (!customCurrency ? " ZANO" : "")}</p> ? <p>{amount + (!customCurrency ? " ZANO" : "")}</p>
: amount : amount
} }
{percent && {percent &&
<div className="item__precent"> <div className={styles["item__precent"]}>
<div> <div>
<p> <p>
{percent + "%"} {percent + "%"}
</p> </p>
</div> </div>
<p className="item__precent__label">from total supply</p> <p className={styles["item__precent__label"]}>from total supply</p>
</div> </div>
} }
</div> </div>
@ -79,16 +77,15 @@ function StatsPanel(props: { visibilityInfo?: VisibilityInfo | null, noStats?: b
) )
} }
function BottomItem(props: { title: string, children: React.ReactNode, style?: React.CSSProperties }) { function BottomItem(props: { title: string, children: React.ReactNode, style?: React.CSSProperties }) {
const { title, children, style } = props; const { title, children, style } = props;
return ( return (
<div className="main__bottom__item" style={style}> <div className={styles["main__bottom__item"]} style={style}>
<div> <div>
<p>{title}</p> <p>{title}</p>
</div> </div>
<div className="bottom__item__value"> <div className={styles["bottom__item__value"]}>
{children} {children}
</div> </div>
</div> </div>
@ -97,44 +94,44 @@ function StatsPanel(props: { visibilityInfo?: VisibilityInfo | null, noStats?: b
function InfoTop() { function InfoTop() {
return ( return (
<div className="info__main__top"> <div className={styles["info__main__top"]}>
<TopItem <TopItem
title="Staked Coins (est)" title="Staked Coins (est)"
amount={stackedCoins} amount={stackedCoins}
percent={percentage} percent={percentage}
/> />
<TopItem <TopItem
title="Dev Fund" title="Dev Fund"
amount={devFund} amount={devFund}
/> />
<TopItem <TopItem
title="Real Time APY" title="Real Time APY"
amount={`${APY}%`} amount={`${APY}%`}
customCurrency={true} customCurrency={true}
/> />
<TopItem <TopItem
title="Zano Burned" title="Zano Burned"
amount={ amount={
<div className="item__value__burn"> <div className={styles["item__value__burn"]}>
<BurnImg /> <BurnImg />
<p>{zanoBurned} ZANO</p> <p>{zanoBurned} ZANO</p>
</div> </div>
} }
/> />
</div> </div>
); );
} }
return ( return (
<> <>
<div className="blockchain__info__main"> <div className={styles["blockchain__info__main"]}>
{NET_MODE === "MAIN" && !props.noStats && <InfoTop/>} {NET_MODE === "MAIN" && !props.noStats && <InfoTop/>}
<div className="info__main__bottom"> <div className={styles["info__main__bottom"]}>
<BottomItem title="Height"> <BottomItem title="Height">
<p className="item__text__large">{infoHeight}</p> <p className={styles["item__text__large"]}>{infoHeight}</p>
</BottomItem> </BottomItem>
<BottomItem title="Difficulty"> <BottomItem title="Difficulty">
<div className="item__difficulty"> <div className={styles["item__difficulty"]}>
<div> <div>
<p>PoS: {posDiff}</p> <p>PoS: {posDiff}</p>
</div> </div>
@ -145,13 +142,13 @@ function StatsPanel(props: { visibilityInfo?: VisibilityInfo | null, noStats?: b
</div> </div>
</BottomItem> </BottomItem>
<BottomItem title="Coins Emitted"> <BottomItem title="Coins Emitted">
<p className="item__text__small">{coinsEmitted}</p> <p className={styles["item__text__small"]}>{coinsEmitted}</p>
</BottomItem> </BottomItem>
<BottomItem title="Transactions"> <BottomItem title="Transactions">
<p className="item__text__large">{transactionsString}</p> <p className={styles["item__text__large"]}>{transactionsString}</p>
</BottomItem> </BottomItem>
<BottomItem title="Hash Rate (approx):"> <BottomItem title="Hash Rate (approx):">
<div className="item__difficulty"> <div className={styles["item__difficulty"]}>
<div> <div>
<p>PoS: {posValue} block/day</p> <p>PoS: {posValue} block/day</p>
</div> </div>
@ -164,16 +161,14 @@ function StatsPanel(props: { visibilityInfo?: VisibilityInfo | null, noStats?: b
</div> </div>
</div> </div>
<div className={styles["info__main__mobile"]}>
<div className="info__main__mobile">
{NET_MODE === "MAIN" && <InfoTop/>} {NET_MODE === "MAIN" && <InfoTop/>}
<div className="info__main__bottom"> <div className={styles["info__main__bottom"]}>
<BottomItem title="Height"> <BottomItem title="Height">
<p className="item__text__large">{infoHeight}</p> <p className={styles["item__text__large"]}>{infoHeight}</p>
</BottomItem> </BottomItem>
<BottomItem title="Difficulty"> <BottomItem title="Difficulty">
<div className="item__difficulty"> <div className={styles["item__difficulty"]}>
<div> <div>
<p>PoS: {posDiff}</p> <p>PoS: {posDiff}</p>
</div> </div>
@ -184,14 +179,13 @@ function StatsPanel(props: { visibilityInfo?: VisibilityInfo | null, noStats?: b
</div> </div>
</BottomItem> </BottomItem>
<BottomItem title="Coins Emitted"> <BottomItem title="Coins Emitted">
<p className="item__text__small">{coinsEmitted}</p> <p className={styles["item__text__small"]}>{coinsEmitted}</p>
</BottomItem> </BottomItem>
<BottomItem title="Transactions"> <BottomItem title="Transactions">
<p className="item__text__large">{transactionsString}</p> <p className={styles["item__text__large"]}>{transactionsString}</p>
</BottomItem> </BottomItem>
<BottomItem title="Hash Rate (approx):"> <BottomItem title="Hash Rate (approx):">
<div className={styles["item__difficulty"]}>
<div className="item__difficulty">
<div> <div>
<p>PoS: {posValue} block/day</p> <p>PoS: {posValue} block/day</p>
</div> </div>
@ -207,4 +201,4 @@ function StatsPanel(props: { visibilityInfo?: VisibilityInfo | null, noStats?: b
) )
} }
export default StatsPanel; export default StatsPanel;

View file

@ -1,5 +1,5 @@
import "./Table.scss"; import styles from "./Table.module.scss";
import { ReactComponent as ArrowImg } from "../../../assets/images/UI/arrow.svg"; import ArrowImg from "../../../assets/images/UI/arrow.svg";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import Input from "../../UI/Input/Input"; import Input from "../../UI/Input/Input";
import TableProps from "./Table.props"; import TableProps from "./Table.props";
@ -42,7 +42,7 @@ function Table(props: TableProps) {
return ( return (
<div className={className}> <div className={className}>
<table className={"table " + (!textWrap ? "table__text_nowrap" : "")}> <table className={`${styles.table} ${!textWrap ? styles["table__text_nowrap"] : ""}`}>
<thead> <thead>
<tr> <tr>
{ {
@ -69,12 +69,12 @@ function Table(props: TableProps) {
</tbody> </tbody>
</table> </table>
{pagination && {pagination &&
<div className="table__pagination"> <div className={styles["table__pagination"]}>
<div className="table__pagination__pages"> <div className={styles["table__pagination__pages"]}>
<p>Pages: </p> <p>Pages: </p>
<div> <div>
<button <button
className={page === "1" ? "disabled" : undefined} className={page === "1" ? styles.disabled : undefined}
onClick={() => changePage(-1)} onClick={() => changePage(-1)}
> >
<ArrowImg /> <ArrowImg />
@ -92,7 +92,7 @@ function Table(props: TableProps) {
/> />
</div> </div>
{pagesTotal && <p>Pages total: {pagesTotal}</p>} {pagesTotal && <p>Pages total: {pagesTotal}</p>}
<div className="table__pagination__blocks"> <div className={styles["table__pagination__blocks"]}>
<div> <div>
<p>Items on page: </p> <p>Items on page: </p>
<Input <Input

View file

@ -1,19 +0,0 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.scss';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

View file

@ -1,13 +1,12 @@
import AliasText from "../../../../components/default/AliasText/AliasText"; import AliasText from "@/components/default/AliasText/AliasText";
import Table from "../../../../components/default/Table/Table"; import Table from "@/components/default/Table/Table";
import Block from "../../../../interfaces/state/Block"; import Block from "@/interfaces/state/Block";
import Info from "../../../../interfaces/state/Info"; import Info from "@/interfaces/state/Info";
import Fetch from "../../../../utils/methods"; import Fetch from "@/utils/methods";
import Utils from "../../../../utils/utils"; import Utils from "@/utils/utils";
import "./LatestBlocks.scss"; import styles from "./LatestBlocks.module.scss";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { socket } from "../../../../utils/socket"; import Link from "next/link";
import { Link } from "react-router-dom";
function LatestBlocks() { function LatestBlocks() {
@ -83,19 +82,19 @@ function LatestBlocks() {
const hashLink = hash ? "/block/" + hash : "/"; const hashLink = hash ? "/block/" + hash : "/";
return [ return [
<p> <p>
<Link to={hashLink}>{e.height}</Link> <Link href={hashLink}>{e.height}</Link>
{` (${e.type})`} {` (${e.type})`}
</p>, </p>,
Utils.formatTimestampUTC(e.timestamp), Utils.formatTimestampUTC(e.timestamp),
Utils.timeElapsedString(e.timestamp), Utils.timeElapsedString(e.timestamp),
`${e.size} bytes`, `${e.size} bytes`,
e.transactions?.toString() || "0", e.transactions?.toString() || "0",
<AliasText to={hashLink}>{hash}</AliasText> <AliasText href={hashLink}>{hash}</AliasText>
] ]
}); });
return ( return (
<div className="blockchain__latest_blocks custom-scroll"> <div className={styles["blockchain__latest_blocks"] + " " + styles["custom-scroll"]}>
<h3>Latest Blocks</h3> <h3>Latest Blocks</h3>
<Table <Table
pagination pagination

View file

@ -1,9 +1,9 @@
import "./TransactionPool.scss"; import styles from "./TransactionPool.module.scss";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import Button from "../../../../components/UI/Button/Button"; import Button from "@/components/UI/Button/Button";
import Table from "../../../../components/default/Table/Table"; import Table from "@/components/default/Table/Table";
import AliasText from "../../../../components/default/AliasText/AliasText"; import AliasText from "@/components/default/AliasText/AliasText";
import { socket } from "../../../../utils/socket"; import { socket } from "@/utils/socket";
function TransactionPool() { function TransactionPool() {
@ -78,12 +78,12 @@ function TransactionPool() {
timeAgo(new Date(element.timestamp).getTime()), timeAgo(new Date(element.timestamp).getTime()),
element.blob_size + " bytes", element.blob_size + " bytes",
parseInt(element.fee, 10)/10**12, parseInt(element.fee, 10)/10**12,
<AliasText to={`/transaction/${element.tx_id}`}>{element.tx_id}</AliasText> <AliasText href={`/transaction/${element.tx_id}`}>{element.tx_id}</AliasText>
])); ]));
return ( return (
<div className="transaction_pool custom-scroll"> <div className={styles["transaction_pool"] + " custom-scroll"}>
<div className="transation_pool__title"> <div className={styles["transation_pool__title"]}>
<h3>Transaction Pool</h3> <h3>Transaction Pool</h3>
<Button <Button
style={ turnedOn ? { color: "#ff5252" } : { color: "#00c853" }} style={ turnedOn ? { color: "#ff5252" } : { color: "#00c853" }}
@ -93,11 +93,11 @@ function TransactionPool() {
</Button> </Button>
</div> </div>
{!turnedOn || !tableElements[0] ? {!turnedOn || !tableElements[0] ?
<div className="transation_pool__empty"> <div className={styles["transation_pool__empty"]}>
<p>Pool is empty</p> <p>Pool is empty</p>
</div> : </div> :
<Table <Table
className="transaction__table" className={styles["transaction__table"]}
headers={tableHeaders} headers={tableHeaders}
elements={tableElements} elements={tableElements}
/> />

View file

@ -1,12 +1,12 @@
import "../../styles/Blockchain.scss"; import styles from "@/styles/Blockchain.module.scss";
import Header from "../../components/default/Header/Header"; import Header from "@/components/default/Header/Header";
import StatsPanel from "../../components/default/StatsPanel/StatsPanel"; import StatsPanel from "@/components/default/StatsPanel/StatsPanel";
import InfoTopPanel from "../../components/default/InfoTopPanel/InfoTopPanel"; import InfoTopPanel from "@/components/default/InfoTopPanel/InfoTopPanel";
import LatestBlocks from "./components/LatestBlocks/LatestBlocks"; import LatestBlocks from "./components/LatestBlocks/LatestBlocks";
import TransactionPool from "./components/TransactionPool/TransactionPool"; import TransactionPool from "./components/TransactionPool/TransactionPool";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Fetch from "../../utils/methods"; import Fetch from "@/utils/methods";
import VisibilityInfo from "../../interfaces/state/VisibilityInfo"; import VisibilityInfo from "@/interfaces/state/VisibilityInfo";
function Blockchain() { function Blockchain() {
const [burgerOpened, setBurgerOpened] = useState(false); const [burgerOpened, setBurgerOpened] = useState(false);
@ -41,7 +41,7 @@ function Blockchain() {
}, []); }, []);
return ( return (
<div className="blockchain"> <div className={styles["blockchain"]}>
<Header <Header
page="Blockchain" page="Blockchain"
burgerOpened={burgerOpened} burgerOpened={burgerOpened}
@ -51,7 +51,7 @@ function Blockchain() {
burgerOpened={burgerOpened} burgerOpened={burgerOpened}
title="Blockchain" title="Blockchain"
content={ content={
<div className="info__top__daemon"> <div className={styles["info__top__daemon"]}>
<p>Daemon state: {isOnline ? 'Online' : 'Offline'}</p> <p>Daemon state: {isOnline ? 'Online' : 'Offline'}</p>
<p>Default network fee: 0,01</p> <p>Default network fee: 0,01</p>
<p>Minimum network fee: 0,01</p> <p>Minimum network fee: 0,01</p>

View file

@ -1,14 +1,14 @@
import "../../styles/ChartPage.scss"; import styles from "@/styles/ChartPage.module.scss";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Header from "../../components/default/Header/Header"; import Header from "@/components/default/Header/Header";
import InfoTopPanel from "../../components/default/InfoTopPanel/InfoTopPanel"; import InfoTopPanel from "@/components/default/InfoTopPanel/InfoTopPanel";
import HighchartsReact from "highcharts-react-official"; import HighchartsReact from "highcharts-react-official";
import Highcharts from "highcharts/highstock"; import Highcharts from "highcharts/highstock";
import { chartFontColor, chartOptions, chartRequestNames } from "../../utils/constants"; import { chartFontColor, chartOptions, chartRequestNames } from "@/utils/constants";
import { useNavigate, useParams } from "react-router-dom"; import ChartSeriesElem from "@/interfaces/common/ChartSeriesElem";
import ChartSeriesElem from "../../interfaces/common/ChartSeriesElem"; import Utils from "@/utils/utils";
import Utils from "../../utils/utils"; import Preloader from "@/components/UI/Preloader/Preloader";
import Preloader from "../../components/UI/Preloader/Preloader"; import { useRouter } from "next/router";
interface ChartInfo { interface ChartInfo {
title: string; title: string;
@ -18,7 +18,6 @@ interface ChartInfo {
function ChartPage() { function ChartPage() {
const chartsInfo: { [key: string]: ChartInfo } = { const chartsInfo: { [key: string]: ChartInfo } = {
"avg-block-size": { "avg-block-size": {
title: "Average Block Size", title: "Average Block Size",
@ -58,16 +57,16 @@ function ChartPage() {
// The only values that chartId param can have are the keys of the chartRequestNames object. // The only values that chartId param can have are the keys of the chartRequestNames object.
// Other values will cause redirect to the home page. // Other values will cause redirect to the home page.
const { name: chartId } = useParams(); const router = useRouter();
const { name } = router.query;
const chartId: string | undefined = Array.isArray(name) ? name[0] : name;
const [chartSeries, setChartSeries] = useState<ChartSeriesElem[][]>([]); const [chartSeries, setChartSeries] = useState<ChartSeriesElem[][]>([]);
const chartSeriesTitles = [ const chartSeriesTitles = [
chartsInfo[chartId || ""]?.seriesTitle || "", chartsInfo[chartId || ""]?.seriesTitle || "",
"Hash Rate 400", "Hash Rate 400",
"Difficulty 120" "Difficulty 120"
] ];
const navigate = useNavigate();
useEffect(() => { useEffect(() => {
async function fetchChart() { async function fetchChart() {
@ -80,14 +79,14 @@ function ChartPage() {
} }
if (!(chartId && chartRequestNames[chartId])) { if (!(chartId && chartRequestNames[chartId])) {
navigate("/"); router.push("/");
} else { } else {
fetchChart(); fetchChart();
} }
}, [chartId]); }, [chartId]);
return ( return (
<div className="chart_page"> <div className={styles["chart_page"]}>
<Header <Header
page="Charts" page="Charts"
burgerOpened={burgerOpened} burgerOpened={burgerOpened}
@ -98,11 +97,11 @@ function ChartPage() {
title="Charts" title="Charts"
back back
/> />
<div className="chart_page__chart__wrapper"> <div className={styles["chart_page__chart__wrapper"]}>
{ {
loading ? loading ?
( (
<div className="chart_page__preloader"> <div className={styles["chart_page__preloader"]}>
<Preloader /> <Preloader />
</div> </div>
) )
@ -133,7 +132,7 @@ function ChartPage() {
}, },
chart: { chart: {
...chartOptions.chart, ...chartOptions.chart,
className: "chart_page__chart", className: styles["chart_page__chart"],
height: 700, height: 700,
}, },
rangeSelector: { rangeSelector: {
@ -178,4 +177,4 @@ function ChartPage() {
) )
} }
export default ChartPage; export default ChartPage;

View file

@ -1,14 +1,14 @@
import "../../styles/Charts.scss"; import styles from "@/styles/Charts.module.scss";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import Header from "../../components/default/Header/Header"; import Header from "@/components/default/Header/Header";
import InfoTopPanel from "../../components/default/InfoTopPanel/InfoTopPanel"; import InfoTopPanel from "@/components/default/InfoTopPanel/InfoTopPanel";
import HighchartsReact from "highcharts-react-official"; import HighchartsReact from "highcharts-react-official";
import Highcharts from "highcharts"; import Highcharts from "highcharts";
import { chartOptions } from "../../utils/constants"; import { chartOptions } from "@/utils/constants";
import Utils from "../../utils/utils"; import Utils from "@/utils/utils";
import ChartSeriesElem from "../../interfaces/common/ChartSeriesElem"; import ChartSeriesElem from "@/interfaces/common/ChartSeriesElem";
import Preloader from "../../components/UI/Preloader/Preloader"; import Preloader from "@/components/UI/Preloader/Preloader";
import { Link } from "react-router-dom"; import Link from "next/link";
function Charts() { function Charts() {
const [burgerOpened, setBurgerOpened] = useState(false); const [burgerOpened, setBurgerOpened] = useState(false);
@ -66,8 +66,8 @@ function Charts() {
} = props; } = props;
return ( return (
<Link to={"/charts/" + requestTitle} className="charts__chart__wrapper"> <Link href={"/charts/" + requestTitle} className={styles["charts__chart__wrapper"]}>
<div className="charts__chart__title"> <div className={styles["charts__chart__title"]}>
<p>{title}</p> <p>{title}</p>
</div> </div>
<HighchartsReact <HighchartsReact
@ -86,7 +86,7 @@ function Charts() {
chart: { chart: {
...chartOptions.chart, ...chartOptions.chart,
height: 280, height: 280,
className: "charts__chart" className: styles["charts__chart"]
}, },
tooltip: { tooltip: {
enabled: false enabled: false
@ -124,7 +124,7 @@ function Charts() {
} }
return ( return (
<div className="charts"> <div className={styles["charts"]}>
<Header <Header
page="Charts" page="Charts"
burgerOpened={burgerOpened} burgerOpened={burgerOpened}
@ -135,7 +135,7 @@ function Charts() {
title="Charts" title="Charts"
/> />
{loaded ? {loaded ?
<div className="charts__container"> <div className={styles["charts__container"]}>
<Chart <Chart
title="Average Block Size" title="Average Block Size"
requestTitle="avg-block-size" requestTitle="avg-block-size"
@ -161,7 +161,7 @@ function Charts() {
requestTitle="confirm-trans-per-day" requestTitle="confirm-trans-per-day"
/> />
</div> : </div> :
<div className="charts__preloader"> <div className={styles["charts__preloader"]}>
<Preloader /> <Preloader />
</div> </div>
} }

32
src/pages/_app.tsx Normal file
View file

@ -0,0 +1,32 @@
import './index.scss';
import { default as NextApp } from 'next/app';
import Head from 'next/head';
function App(data: any) {
const { Component, pageProps } = data;
return (
<>
<Head>
<title>Zano Trade</title>
<meta name="description" content="Peer-to-Peer Trading App on Zano blockchain" />
<meta name="viewport" content="width=device-width, initial-scale=1 user-scalable=no" />
<link rel="icon" href="/favicon.ico" />
<meta property="og:type" content="website"/>
<meta property="og:url" content="https://trade.zano.org/"/>
<meta property="og:title" content="Zano Trade"/>
<meta property="og:description" content="Peer-to-Peer Trading App on Zano blockchain"/>
<meta property="og:image" content="social-banner.png"/>
<meta property="twitter:card" content="summary_large_image"/>
<meta property="twitter:url" content="https://trade.zano.org/"/>
<meta property="twitter:title" content="Zano Trade"/>
<meta property="twitter:description" content="Peer-to-Peer Trading App on Zano blockchain"/>
<meta property="twitter:image" content="social-banner.png"/>
</Head>
<Component {...pageProps} />
</>
);
}
export default App;

15
src/pages/_document.js Normal file
View file

@ -0,0 +1,15 @@
import { Html, Head, Main, NextScript } from 'next/document';
function Document() {
return (
<Html lang="en">
<Head />
<body className='custom-scroll'>
<Main />
<NextScript />
</body>
</Html>
)
}
export default Document;

View file

@ -1,11 +1,11 @@
import "../../styles/Aliases.scss"; import styles from "@/styles/Aliases.module.scss";
import { useState, useEffect, useCallback } from "react"; import { useState, useEffect, useCallback } from "react";
import Header from "../../components/default/Header/Header"; import Header from "../../components/default/Header/Header";
import InfoTopPanel from "../../components/default/InfoTopPanel/InfoTopPanel"; import InfoTopPanel from "../../components/default/InfoTopPanel/InfoTopPanel";
import Table from "../../components/default/Table/Table"; import Table from "../../components/default/Table/Table";
import Alias from "../../interfaces/state/Alias"; import Alias from "../../interfaces/state/Alias";
import Fetch from "../../utils/methods"; import Fetch from "../../utils/methods";
import crownImg from "../../assets/images/UI/crown.svg"; import CrownImg from "../../assets/images/UI/crown.svg";
import CommonStatsPanel from "../../components/UI/CommonStatsPanel/CommonStatsPanel"; import CommonStatsPanel from "../../components/UI/CommonStatsPanel/CommonStatsPanel";
function Aliases() { function Aliases() {
@ -63,10 +63,10 @@ function Aliases() {
function ShortAlias({ alias }: { alias: string }) { function ShortAlias({ alias }: { alias: string }) {
return ( return (
<div> <div>
<div className="short_alias"> <div className={styles["short_alias"]}>
{alias} {alias}
<div className="short_alias__crown"> <div className={styles["short_alias__crown"]}>
<img src={crownImg} alt="" /> <img src={CrownImg} alt="" />
</div> </div>
</div> </div>
</div> </div>
@ -84,7 +84,7 @@ function Aliases() {
]; ];
return ( return (
<div className="aliases"> <div className={styles["aliases"]}>
<Header <Header
page="Aliases" page="Aliases"
burgerOpened={burgerOpened} burgerOpened={burgerOpened}
@ -99,8 +99,8 @@ function Aliases() {
setState: setSearchState setState: setSearchState
}} }}
/> />
<CommonStatsPanel pairs={statsPanelData} className="aliases__stats" /> <CommonStatsPanel pairs={statsPanelData} className={styles["aliases__stats"]} />
<div className="aliases__table custom-scroll"> <div className={`${styles["aliases__table"]} custom-scroll`}>
<Table <Table
headers={tableHeaders} headers={tableHeaders}
elements={tableElements} elements={tableElements}

View file

@ -1,14 +1,13 @@
import "../../styles/AltBlocks.scss"; import styles from "@/styles/AltBlocks.module.scss";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import Header from "../../components/default/Header/Header"; import Header from "@/components/default/Header/Header";
import InfoTopPanel from "../../components/default/InfoTopPanel/InfoTopPanel"; import InfoTopPanel from "@/components/default/InfoTopPanel/InfoTopPanel";
import Table from "../../components/default/Table/Table"; import Table from "@/components/default/Table/Table";
import AliasText from "../../components/default/AliasText/AliasText"; import AliasText from "@/components/default/AliasText/AliasText";
import Block from "../../interfaces/state/Block"; import Block from "@/interfaces/state/Block";
import Fetch from "../../utils/methods"; import Fetch from "@/utils/methods";
import Utils from "../../utils/utils"; import Utils from "@/utils/utils";
import { Link } from "react-router-dom"; import Link from "next/link";
function AltBlocks() { function AltBlocks() {
const [burgerOpened, setBurgerOpened] = useState(false); const [burgerOpened, setBurgerOpened] = useState(false);
@ -38,7 +37,7 @@ function AltBlocks() {
return () => clearInterval(id); return () => clearInterval(id);
}, [itemsOnPage, page]); }, [itemsOnPage, page]);
const tableHeaders = [ "HEIGHT", "TIMESTAMP (UTC)", "ACTIAL TIMESTAMP (UTC)", "SIZE", "TRANSACTIONS", "HASH" ]; const tableHeaders = ["HEIGHT", "TIMESTAMP (UTC)", "ACTIAL TIMESTAMP (UTC)", "SIZE", "TRANSACTIONS", "HASH"];
const tableElements = altBlocks.map(e => { const tableElements = altBlocks.map(e => {
const hash = e.hash; const hash = e.hash;
@ -46,31 +45,30 @@ function AltBlocks() {
return [ return [
<p> <p>
<Link to={hashLink}>{e.height}</Link> <Link href={hashLink}>{e.height}</Link>
{` (${e.type})`} {` (${e.type})`}
</p>, </p>,
Utils.formatTimestampUTC(e.timestamp), Utils.formatTimestampUTC(e.timestamp),
Utils.formatTimestampUTC(e.timestamp), Utils.formatTimestampUTC(e.timestamp),
`${e.size} bytes`, `${e.size} bytes`,
e.transactions?.toString() || "0", e.transactions?.toString() || "0",
<AliasText to={hashLink}>{e.hash}</AliasText> <AliasText href={hashLink}>{e.hash}</AliasText>
] ]
}); });
return ( return (
<div className="alt_blocks"> <div className={styles["alt_blocks"]}>
<Header <Header
page="Alt-blocks" page="Alt-blocks"
burgerOpened={burgerOpened} burgerOpened={burgerOpened}
setBurgerOpened={setBurgerOpened} setBurgerOpened={setBurgerOpened}
/> />
<InfoTopPanel <InfoTopPanel
burgerOpened={burgerOpened} burgerOpened={burgerOpened}
title="Alt-blocks" title="Alt-blocks"
/> />
<div className="alt_blocks__table custom-scroll"> <div className={`${styles["alt_blocks__table"]} custom-scroll`}>
<Table <Table
headers={tableHeaders} headers={tableHeaders}
elements={tableElements} elements={tableElements}
pagination pagination
@ -85,4 +83,4 @@ function AltBlocks() {
) )
} }
export default AltBlocks; export default AltBlocks;

View file

@ -1,8 +1,8 @@
import "../../styles/Asset.scss"; import styles from "@/styles/Asset.module.scss";
import { useState } from "react"; import { useState } from "react";
import Header from "../../components/default/Header/Header"; import Header from "@/components/default/Header/Header";
import InfoTopPanel from "../../components/default/InfoTopPanel/InfoTopPanel"; import InfoTopPanel from "@/components/default/InfoTopPanel/InfoTopPanel";
import Table from "../../components/default/Table/Table"; import Table from "@/components/default/Table/Table";
export default function Asset() { export default function Asset() {
const [burgerOpened, setBurgerOpened] = useState(false); const [burgerOpened, setBurgerOpened] = useState(false);
@ -27,13 +27,13 @@ export default function Asset() {
burgerOpened={burgerOpened} burgerOpened={burgerOpened}
title="" title=""
back back
className="block__info__top" className={styles["block__info__top"]}
/> />
<Table <Table
columnsWidth={[50, 50]} columnsWidth={[50, 50]}
headers={["NAME", "AMOUNT"]} headers={["NAME", "AMOUNT"]}
elements={assetsRows} elements={assetsRows}
className="asset__table" className={styles["asset__table"]}
/> />
</div> </div>
) )

View file

@ -1,17 +1,18 @@
import "../../styles/Assets.scss"; import styles from "@/styles/Assets.module.scss";
import { useState, useEffect, useRef, memo, useCallback } from "react"; import { useState, useEffect, useRef, memo, useCallback } from "react";
import Header from "../../components/default/Header/Header"; import Header from "@/components/default/Header/Header";
import InfoTopPanel from "../../components/default/InfoTopPanel/InfoTopPanel"; import InfoTopPanel from "@/components/default/InfoTopPanel/InfoTopPanel";
import Table from "../../components/default/Table/Table"; import Table from "@/components/default/Table/Table";
import Fetch from "../../utils/methods"; import Fetch from "@/utils/methods";
import AliasText from "../../components/default/AliasText/AliasText"; import AliasText from "@/components/default/AliasText/AliasText";
import JSONPopup from "../../components/default/JSONPopup/JSONPopup"; import JSONPopup from "@/components/default/JSONPopup/JSONPopup";
import Switch from "../../components/UI/Switch/Switch"; import Switch from "@/components/UI/Switch/Switch";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import Button from "../../components/UI/Button/Button"; import Button from "@/components/UI/Button/Button";
import { useSearchParams } from "react-router-dom"; import CopyButton from "@/components/UI/CopyButton/CopyButton";
import CopyButton from "../../components/CopyButton/CopyButton"; import CommonStatsPanel from "@/components/UI/CommonStatsPanel/CommonStatsPanel";
import CommonStatsPanel from "../../components/UI/CommonStatsPanel/CommonStatsPanel"; import { useRouter } from "next/router";
import { useSearchParams } from "next/navigation";
const AssetPopupBottom = memo(({ assetId }: { assetId?: string }) => { const AssetPopupBottom = memo(({ assetId }: { assetId?: string }) => {
@ -35,11 +36,11 @@ const AssetPopupBottom = memo(({ assetId }: { assetId?: string }) => {
return ( return (
assetLink ? ( assetLink ? (
<div className="asset_popup__bottom"> <div className={styles["asset_popup__bottom"]}>
<Button onClick={copy}>{clicked ? 'Copied' : 'Copy asset link'}</Button> <Button onClick={copy}>{clicked ? 'Copied' : 'Copy asset link'}</Button>
</div> </div>
) : ( ) : (
<div className="asset_popup__not_found"> <div className={styles["asset_popup__not_found"]}>
<h3> <h3>
Asset not found or does not exist. For newly created assets, please allow one minute to appear in the explorer Asset not found or does not exist. For newly created assets, please allow one minute to appear in the explorer
</h3> </h3>
@ -49,8 +50,8 @@ const AssetPopupBottom = memo(({ assetId }: { assetId?: string }) => {
}); });
function Assets() { function Assets() {
const searchParams = useSearchParams();
const [searchParams, setSearchParams] = useSearchParams(); const router = useRouter();
const ZANO_ID = const ZANO_ID =
"d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a"; "d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a";
@ -119,7 +120,10 @@ function Assets() {
useEffect(() => { useEffect(() => {
async function fetchParamAsset() { async function fetchParamAsset() {
const assetId = decodeURIComponent(searchParams.get("asset_id") || ''); const assetId = decodeURIComponent(
searchParams.get('asset_id') || ""
);
if (assetId) { if (assetId) {
const response = await Fetch.getAssetDetails(assetId); const response = await Fetch.getAssetDetails(assetId);
@ -196,9 +200,9 @@ function Assets() {
const tableElements = assets.map(e => [ const tableElements = assets.map(e => [
e?.full_name || "", e?.full_name || "",
e?.ticker || "", e?.ticker || "",
<div className="assets__table_asset-id"> <div className={styles["assets__table_asset-id"]}>
<CopyButton text={e?.asset_id || ""} /> <CopyButton text={e?.asset_id || ""} />
<AliasText to="/" onClick={(event) => onAssetClick(event, e)}> <AliasText href="/" onClick={(event) => onAssetClick(event, e)}>
{e?.asset_id || ""} {e?.asset_id || ""}
</AliasText> </AliasText>
@ -212,7 +216,7 @@ function Assets() {
]; ];
return ( return (
<div className="assets"> <div className={styles["assets"]}>
<Header <Header
page="Assets" page="Assets"
burgerOpened={burgerOpened} burgerOpened={burgerOpened}
@ -237,8 +241,8 @@ function Assets() {
/> />
} }
/> />
<CommonStatsPanel pairs={statsPanelData} className={"assets__stats"} /> <CommonStatsPanel pairs={statsPanelData} className={styles["assets__stats"]} />
<div className="assets__table"> <div className={styles["assets__table"]}>
<Table <Table
headers={tableHeaders} headers={tableHeaders}
elements={tableElements} elements={tableElements}
@ -265,9 +269,15 @@ function Assets() {
/> />
), ),
onClose: () => { onClose: () => {
if (searchParams.get("asset_id")) { if (searchParams.get('asset_id')) {
searchParams.delete("asset_id"); const removeQueryParam = (param: string) => {
setSearchParams(searchParams); const updatedQuery = router.query;
delete updatedQuery[param];
router.push({ query: updatedQuery }, undefined, { shallow: true });
}
removeQueryParam('asset_id');
} }
setNotFountPopupState(false); setNotFountPopupState(false);
} }

View file

@ -1,15 +1,16 @@
import "../../styles/Block.scss"; import styles from "@/styles/Block.module.scss";
import Header from "../../components/default/Header/Header"; import Header from "@/components/default/Header/Header";
import InfoTopPanel from "../../components/default/InfoTopPanel/InfoTopPanel"; import InfoTopPanel from "@/components/default/InfoTopPanel/InfoTopPanel";
import StatsPanel from "../../components/default/StatsPanel/StatsPanel"; import StatsPanel from "@/components/default/StatsPanel/StatsPanel";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Table from "../../components/default/Table/Table"; import Table from "@/components/default/Table/Table";
import { ReactComponent as ArrowImg } from "../../assets/images/UI/arrow.svg"; import ArrowImg from "@/assets/images/UI/arrow.svg";
import BlockInfo from "../../interfaces/common/BlockInfo"; import BlockInfo from "@/interfaces/common/BlockInfo";
import Utils from "../../utils/utils"; import Utils from "@/utils/utils";
import { Link, useParams } from "react-router-dom"; import Link from "next/link";
import Fetch from "../../utils/methods"; import Fetch from "@/utils/methods";
import Popup from "../../components/default/Popup/Popup"; import Popup from "@/components/default/Popup/Popup";
import { useRouter } from "next/router";
interface Transaction { interface Transaction {
hash: string; hash: string;
@ -24,27 +25,26 @@ function Block(props: { alt?: boolean }) {
const [burgerOpened, setBurgerOpened] = useState(false); const [burgerOpened, setBurgerOpened] = useState(false);
const [jsonPopupOpened, setJsonPopupOpened] = useState(false); const [jsonPopupOpened, setJsonPopupOpened] = useState(false);
const { hash } = useParams(); const router = useRouter();
const { hashQuery } = router.query;
const tableHeaders = [ "HASH", "FEE", "TOTAL AMOUNT", "SIZE" ]; const hash = Array.isArray(hashQuery) ? hashQuery[0] : hashQuery;
const tableHeaders = ["HASH", "FEE", "TOTAL AMOUNT", "SIZE"];
const [blockInfo, setBlockInfo] = useState<BlockInfo | null>(null); const [blockInfo, setBlockInfo] = useState<BlockInfo | null>(null);
const [transactions, setTransactions] = useState<Transaction[]>([]); const [transactions, setTransactions] = useState<Transaction[]>([]);
const tableElements = transactions.map(e => [ const tableElements = transactions.map(e => [
!alt !alt
? ? (
( <Link href={"/transaction/" + (e.hash || "")} className={styles["block__table__hash"]}>
<Link to={"/transaction/" + (e.hash || "")} className="block__table__hash"> {e.hash}
{e.hash} </Link>
</Link> )
) : (
: <p className={styles["block__table__hash"]}>{e.hash}</p>
( ),
<p className="block__table__hash">{e.hash}</p>
),
e.fee, e.fee,
e.amount, e.amount,
e.size + " bytes" e.size + " bytes"
@ -63,7 +63,7 @@ function Block(props: { alt?: boolean }) {
setPrevHash(prevHashFetched); setPrevHash(prevHashFetched);
setNextHash(nextHashFetched); setNextHash(nextHashFetched);
} }
fetchHash(); fetchHash();
}, [height]); }, [height]);
@ -78,8 +78,7 @@ function Block(props: { alt?: boolean }) {
setHeight(result.height || null); setHeight(result.height || null);
console.log(result); console.log(result);
setBlockInfo({ setBlockInfo({
type: result.type === "1" ? "PoW" : "PoS", type: result.type === "1" ? "PoW" : "PoS",
timestamp: result.timestamp || undefined, timestamp: result.timestamp || undefined,
@ -100,24 +99,24 @@ function Block(props: { alt?: boolean }) {
currentTxsMedian: undefined, currentTxsMedian: undefined,
transactions: result.tr_count || "0", transactions: result.tr_count || "0",
transactionsSize: result.total_txs_size || "0", transactionsSize: result.total_txs_size || "0",
alreadyGeneratedCoins: result.already_generated_coins || undefined, alreadyGeneratedCoins: result.already_generated_coins || undefined,
object_in_json: result.object_in_json || undefined, object_in_json: result.object_in_json || undefined,
tx_id: result.tx_id || undefined, tx_id: result.tx_id || undefined,
prev_id: result.prev_id || undefined, prev_id: result.prev_id || undefined,
minor_version: result?.object_in_json?.split('\"minor_version\": ')?.[1]?.split(',')?.[0] || '-', minor_version: result?.object_in_json?.split('\"minor_version\": ')?.[1]?.split(',')?.[0] || '-',
major_version: result?.object_in_json?.split('\"major_version\": ')?.[1]?.split(',')?.[0] || '-', major_version: result?.object_in_json?.split('\"major_version\": ')?.[1]?.split(',')?.[0] || '-',
}); });
const rawTransactionsDetails = result.transactions_details; const rawTransactionsDetails = result.transactions_details;
const transactionsDetails = const transactionsDetails =
typeof rawTransactionsDetails === "string" typeof rawTransactionsDetails === "string"
? (() => { ? (() => {
try { try {
return JSON.parse(rawTransactionsDetails); return JSON.parse(rawTransactionsDetails);
} catch {} } catch { }
})() })()
: rawTransactionsDetails; : rawTransactionsDetails;
if (!(transactionsDetails instanceof Array)) return; if (!(transactionsDetails instanceof Array)) return;
@ -133,14 +132,14 @@ function Block(props: { alt?: boolean }) {
fetchBlock(); fetchBlock();
}, [hash]); }, [hash]);
function BlockInfo() { function BlockInfo() {
function JsonPopup() { function JsonPopup() {
return ( return (
<div className="block__info__json"> <div className={styles["block__info__json"]}>
{/* <button>x</button> */} {/* <button>x</button> */}
<div className="block__info__json__content"> <div className={styles["block__info__json__content"]}>
{blockInfo?.object_in_json || ''} {blockInfo?.object_in_json || ''}
</div> </div>
</div> </div>
@ -148,9 +147,9 @@ function Block(props: { alt?: boolean }) {
} }
return ( return (
<div className="block__info"> <div className={styles["block__info"]}>
{jsonPopupOpened && {jsonPopupOpened &&
<Popup <Popup
Content={JsonPopup} Content={JsonPopup}
close={() => setJsonPopupOpened(false)} close={() => setJsonPopupOpened(false)}
settings={{ settings={{
@ -158,34 +157,34 @@ function Block(props: { alt?: boolean }) {
}} }}
scroll scroll
blur blur
classList={["block__json_popup"]} classList={[styles["block__json_popup"]]}
/> />
} }
<div className="block__info__title"> <div className={styles["block__info__title"]}>
<h2>Zano Block</h2> <h2>Zano Block</h2>
<div> <div>
{!alt && prevHash !== "" && {!alt && prevHash !== "" &&
<Link to={prevHash ? "/block/" + prevHash : "/"}> <Link href={prevHash ? "/block/" + prevHash : "/"}>
<ArrowImg /> <ArrowImg />
</Link> </Link>
} }
<h2>{height}</h2> <h2>{height}</h2>
{!alt && nextHash !== "" && {!alt && nextHash !== "" &&
<Link to={nextHash ? "/block/" + nextHash : "/"}> <Link href={nextHash ? "/block/" + nextHash : "/"}>
<ArrowImg /> <ArrowImg />
</Link> </Link>
} }
</div> </div>
<p>{hash?.toUpperCase() || ""}</p> <p>{hash?.toUpperCase() || ""}</p>
</div> </div>
<div className="block__info__table"> <div className={styles["block__info__table"]}>
<table> <table>
<tbody> <tbody>
<tr> <tr>
<td>Type:</td> <td>Type:</td>
<td> <td>
<span <span
className={`block__info__type ${blockInfo?.type === "PoS" ? "type__pos" : "type__pow"}`} className={`${styles["block__info__type"]} ${blockInfo?.type === "PoS" ? styles["type__pos"] : styles["type__pow"]}`}
> >
{blockInfo?.type ?? "-"} {blockInfo?.type ?? "-"}
</span> </span>
@ -197,7 +196,7 @@ function Block(props: { alt?: boolean }) {
</tr> </tr>
<tr> <tr>
<td>ID</td> <td>ID</td>
<td><Link to="/">{Utils.shortenAddress(blockInfo?.tx_id ?? "-")}</Link></td> <td><Link href="/">{Utils.shortenAddress(blockInfo?.tx_id ?? "-")}</Link></td>
</tr> </tr>
<tr> <tr>
<td>Actual Timestamp (UTC):</td> <td>Actual Timestamp (UTC):</td>
@ -249,7 +248,7 @@ function Block(props: { alt?: boolean }) {
</tr> </tr>
<tr> <tr>
<td>Previous ID:</td> <td>Previous ID:</td>
<td><Link to={`/block/${blockInfo?.prev_id}`}>{Utils.shortenAddress(blockInfo?.prev_id ?? "-")}</Link></td> <td><Link href={`/block/${blockInfo?.prev_id}`}>{Utils.shortenAddress(blockInfo?.prev_id ?? "-")}</Link></td>
</tr> </tr>
<tr> <tr>
<td>Total block size, bytes:</td> <td>Total block size, bytes:</td>
@ -287,7 +286,7 @@ function Block(props: { alt?: boolean }) {
<td>JSON data:</td> <td>JSON data:</td>
<td> <td>
<Link <Link
to="/" href="/"
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
setJsonPopupOpened(true); setJsonPopupOpened(true);
@ -301,28 +300,28 @@ function Block(props: { alt?: boolean }) {
</table> </table>
</div> </div>
</div> </div>
) )
} }
return ( return (
<div className="block"> <div className={styles["block"]}>
<Header <Header
page="Blockchain" page="Blockchain"
burgerOpened={burgerOpened} burgerOpened={burgerOpened}
setBurgerOpened={setBurgerOpened} setBurgerOpened={setBurgerOpened}
/> />
<InfoTopPanel <InfoTopPanel
burgerOpened={burgerOpened} burgerOpened={burgerOpened}
title="" title=""
back back
className="block__info__top" className={styles["block__info__top"]}
/> />
<StatsPanel noStats={true}/> <StatsPanel noStats={true} />
<BlockInfo /> <BlockInfo />
<div className="block__transactions"> <div className={styles["block__transactions"]}>
<h2>Transactions</h2> <h2>Transactions</h2>
<Table <Table
headers={tableHeaders} headers={tableHeaders}
elements={tableElements} elements={tableElements}
/> />
@ -331,4 +330,4 @@ function Block(props: { alt?: boolean }) {
) )
} }
export default Block; export default Block;

View file

@ -10,7 +10,7 @@
color: #ffffff; color: #ffffff;
} }
#root { #__next {
max-width: 1200px; max-width: 1200px;
padding: 0 15px; padding: 0 15px;
margin: 0 auto; margin: 0 auto;
@ -72,42 +72,42 @@ button {
@font-face { @font-face {
font-family: 'Open Sans'; font-family: 'Open Sans';
src: url(./assets/fonts/OpenSans/OpenSans-Light.ttf) format("opentype"); src: url(../assets/fonts/OpenSans/OpenSans-Light.ttf) format("opentype");
font-weight: 300; font-weight: 300;
} }
@font-face { @font-face {
font-family: 'Open Sans'; font-family: 'Open Sans';
src: url(./assets/fonts/OpenSans/OpenSans-Regular.ttf) format("opentype"); src: url(../assets/fonts/OpenSans/OpenSans-Regular.ttf) format("opentype");
font-weight: 400; font-weight: 400;
} }
@font-face { @font-face {
font-family: 'Open Sans'; font-family: 'Open Sans';
src: url(./assets/fonts/OpenSans/OpenSans-Medium.ttf) format("opentype"); src: url(../assets/fonts/OpenSans/OpenSans-Medium.ttf) format("opentype");
font-weight: 500; font-weight: 500;
} }
@font-face { @font-face {
font-family: 'Open Sans'; font-family: 'Open Sans';
src: url(./assets/fonts/OpenSans/OpenSans-SemiBold.ttf) format("opentype"); src: url(../assets/fonts/OpenSans/OpenSans-SemiBold.ttf) format("opentype");
font-weight: 600; font-weight: 600;
} }
@font-face { @font-face {
font-family: 'Open Sans'; font-family: 'Open Sans';
src: url(./assets/fonts/OpenSans/OpenSans-Bold.ttf) format("opentype"); src: url(../assets/fonts/OpenSans/OpenSans-Bold.ttf) format("opentype");
font-weight: 700; font-weight: 700;
} }
@font-face { @font-face {
font-family: 'Open Sans'; font-family: 'Open Sans';
src: url(./assets/fonts/OpenSans/OpenSans-ExtraBold.ttf) format("opentype"); src: url(../assets/fonts/OpenSans/OpenSans-ExtraBold.ttf) format("opentype");
font-weight: 800; font-weight: 800;
} }
@font-face { @font-face {
font-family: "Inconsolata"; font-family: "Inconsolata";
src: url(./assets/fonts/Inconsolata/Inconsolata-Regular.ttf) format("opentype"); src: url(../assets/fonts/Inconsolata/Inconsolata-Regular.ttf) format("opentype");
font-weight: 400; font-weight: 400;
} }

9
src/pages/index.tsx Normal file
View file

@ -0,0 +1,9 @@
import Blockchain from './Blockchain/index';
function App() {
return (
<Blockchain />
);
}
export default App;

View file

@ -1,17 +1,18 @@
import "../../styles/Transaction.scss"; import styles from "@/styles/Transaction.module.scss";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Header from "../../components/default/Header/Header"; import Header from "@/components/default/Header/Header";
import InfoTopPanel from "../../components/default/InfoTopPanel/InfoTopPanel"; import InfoTopPanel from "@/components/default/InfoTopPanel/InfoTopPanel";
import StatsPanel from "../../components/default/StatsPanel/StatsPanel"; import StatsPanel from "@/components/default/StatsPanel/StatsPanel";
import Table from "../../components/default/Table/Table"; import Table from "@/components/default/Table/Table";
import TransactionInfo, { Input } from "../../interfaces/common/TransactionInfo"; import TransactionInfo, { Input } from "@/interfaces/common/TransactionInfo";
import Fetch from "../../utils/methods"; import Fetch from "@/utils/methods";
import { Link, useNavigate, useParams } from "react-router-dom"; import Link from "next/link";
import Utils from "../../utils/utils"; import Utils from "@/utils/utils";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import Popup from "../../components/default/Popup/Popup"; import Popup from "@/components/default/Popup/Popup";
import { ReactComponent as CrossImg } from "../../assets/images/UI/cross.svg"; import CrossImg from "@/assets/images/UI/cross.svg";
import Button from "../../components/UI/Button/Button"; import Button from "@/components/UI/Button/Button";
import { useRouter } from "next/router";
interface Block { interface Block {
hash: string; hash: string;
@ -19,8 +20,6 @@ interface Block {
timestamp: string; timestamp: string;
} }
function Transaction() { function Transaction() {
const [burgerOpened, setBurgerOpened] = useState(false); const [burgerOpened, setBurgerOpened] = useState(false);
@ -30,9 +29,11 @@ function Transaction() {
const [popupState, setPopupState] = useState(false); const [popupState, setPopupState] = useState(false);
const [selectedInput, setSelectedInput] = useState<Input | null>(null); const [selectedInput, setSelectedInput] = useState<Input | null>(null);
const { hash } = useParams(); const router = useRouter();
const navigate = useNavigate(); const { hashQuery } = router.query;
const hash = Array.isArray(hashQuery) ? hashQuery[0] : hashQuery;
useEffect(() => { useEffect(() => {
async function fetchTransaction() { async function fetchTransaction() {
@ -81,7 +82,6 @@ function Transaction() {
e.convertedAmount = Utils.convertENotationToString(existingAmount?.toExponential()); e.convertedAmount = Utils.convertENotationToString(existingAmount?.toExponential());
} }
return { return {
amount: e.convertedAmount, amount: e.convertedAmount,
keyimage: e?.kimage_or_ms_id || "", keyimage: e?.kimage_or_ms_id || "",
@ -129,7 +129,7 @@ function Transaction() {
event.preventDefault(); event.preventDefault();
const result = await Fetch.getOutInfo(amount, index); const result = await Fetch.getOutInfo(amount, index);
if (result.tx_id && typeof result.tx_id === "string") { if (result.tx_id && typeof result.tx_id === "string") {
navigate("/transaction/" + result.tx_id); router.push("/transaction/" + result.tx_id);
setPopupState(false); setPopupState(false);
} }
} }
@ -141,12 +141,12 @@ function Transaction() {
e.globalIndexes.length, e.globalIndexes.length,
e.globalIndexes.length > 1 ? e.globalIndexes.length > 1 ?
( (
<Link to="/" onClick={event => showIndexesClick(event, e)}>Show all...</Link> <Link href="/" onClick={event => showIndexesClick(event, e)}>Show all...</Link>
) )
: :
( (
<Link <Link
to="/" href="/"
onClick={event => onIndexClick(event, e.amount, e.globalIndexes[0])} onClick={event => onIndexClick(event, e.amount, e.globalIndexes[0])}
> >
{e.globalIndexes[0] ?? ""} {e.globalIndexes[0] ?? ""}
@ -158,7 +158,7 @@ function Transaction() {
const outsRows = transactionInfo ? ( const outsRows = transactionInfo ? (
transactionInfo.outs.map(e => [ transactionInfo.outs.map(e => [
e.amount, e.amount,
<div className="transaction__outs__keys"> <div className={styles["transaction__outs__keys"]}>
{e.publicKeys.map(e => <p>{e}</p>)} {e.publicKeys.map(e => <p>{e}</p>)}
</div>, </div>,
e.globalIndex e.globalIndex
@ -170,7 +170,7 @@ function Transaction() {
const amount = selectedInput?.amount || 0; const amount = selectedInput?.amount || 0;
return ( return (
<div className="transaction__indexes__popup"> <div className={styles["transaction__indexes__popup"]}>
<h3> <h3>
Input ring set ({popupIndexes.length}) Input ring set ({popupIndexes.length})
</h3> </h3>
@ -178,7 +178,7 @@ function Transaction() {
{popupIndexes.map(e => {popupIndexes.map(e =>
( (
<Link <Link
to="/" href="/"
key={e} key={e}
onClick={event => onIndexClick(event, amount, e)} onClick={event => onIndexClick(event, amount, e)}
> >
@ -189,7 +189,7 @@ function Transaction() {
</div> </div>
<Button <Button
wrapper wrapper
className="popup__cross" className={styles["popup__cross"]}
onClick={close} onClick={close}
> >
<CrossImg /> <CrossImg />
@ -199,7 +199,7 @@ function Transaction() {
} }
return ( return (
<div className="transaction"> <div className={styles["transaction"]}>
<Header <Header
burgerOpened={burgerOpened} burgerOpened={burgerOpened}
setBurgerOpened={setBurgerOpened} setBurgerOpened={setBurgerOpened}
@ -209,10 +209,10 @@ function Transaction() {
burgerOpened={burgerOpened} burgerOpened={burgerOpened}
title="" title=""
back back
className="block__info__top" className={styles["block__info__top"]}
/> />
<StatsPanel noStats={true}/> <StatsPanel noStats={true}/>
<div className="transaction__info"> <div className={styles["transaction__info"]}>
<h2>Transaction</h2> <h2>Transaction</h2>
<table> <table>
<tbody> <tbody>
@ -232,7 +232,7 @@ function Transaction() {
<td>Size</td> <td>Size</td>
<td>{(transactionInfo?.size || "0") + " bytes"}</td> <td>{(transactionInfo?.size || "0") + " bytes"}</td>
</tr> </tr>
<tr className={"transaction__confirmation" + (!transactionInfo?.confirmations ? ' transaction__unconfirmed' : '')}> <tr className={styles["transaction__confirmation"] + (!transactionInfo?.confirmations ? ` ${styles["transaction__unconfirmed"]}` : '')}>
<td>Confirmations</td> <td>Confirmations</td>
<td>{transactionInfo?.confirmations || "Unconfirmed"}</td> <td>{transactionInfo?.confirmations || "Unconfirmed"}</td>
</tr> </tr>
@ -244,7 +244,7 @@ function Transaction() {
<td>Mixin</td> <td>Mixin</td>
<td>{transactionInfo?.mixin || "-"}</td> <td>{transactionInfo?.mixin || "-"}</td>
</tr> </tr>
<tr className="transaction__extra_items"> <tr className={styles["transaction__extra_items"]}>
<td>Extra items</td> <td>Extra items</td>
<td> <td>
{ {
@ -263,15 +263,15 @@ function Transaction() {
</tbody> </tbody>
</table> </table>
</div> </div>
<div className="transaction__table__wrapper"> <div className={styles["transaction__table__wrapper"]}>
<h3>From Block</h3> <h3>From Block</h3>
<Table <Table
className="custom-scroll" className={styles["custom-scroll"]}
headers={[ "HASH", "HEIGHT", "TIMESTAMP (UTC)" ]} headers={[ "HASH", "HEIGHT", "TIMESTAMP (UTC)" ]}
elements={[[ elements={[[
<Link <Link
className="table__hash" className={styles["table__hash"]}
to={blockOrigin?.hash ? "/block/" + blockOrigin.hash : "/"} href={blockOrigin?.hash ? "/block/" + blockOrigin.hash : "/"}
> >
{blockOrigin?.hash} {blockOrigin?.hash}
</Link>, </Link>,
@ -281,19 +281,19 @@ function Transaction() {
columnsWidth={[ 65, 15, 20 ]} columnsWidth={[ 65, 15, 20 ]}
/> />
</div> </div>
<div className="transaction__table__wrapper"> <div className={styles["transaction__table__wrapper"]}>
<h3>{`Inputs ( ${insRows.length} )`}</h3> <h3>{`Inputs ( ${insRows.length} )`}</h3>
<Table <Table
className="transaction__table__inputs custom-scroll" className={`${styles["transaction__table__inputs"]} ${styles["custom-scroll"]}`}
headers={[ "AMOUNT", "IMAGE / MULTISIG ID", "DECOY COUNT", "GLOBAL INDEX" ]} headers={[ "AMOUNT", "IMAGE / MULTISIG ID", "DECOY COUNT", "GLOBAL INDEX" ]}
elements={insRows} elements={insRows}
columnsWidth={[ 15, 50, 15, 20 ]} columnsWidth={[ 15, 50, 15, 20 ]}
/> />
</div> </div>
<div className="transaction__table__wrapper"> <div className={styles["transaction__table__wrapper"]}>
<h3>{`Outputs ( ${outsRows.length} )`}</h3> <h3>{`Outputs ( ${outsRows.length} )`}</h3>
<Table <Table
className="custom-scroll" className={styles["custom-scroll"]}
headers={[ "AMOUNT", "KEY", "GLOBAL INDEX / MULTISIG ID" ]} headers={[ "AMOUNT", "KEY", "GLOBAL INDEX / MULTISIG ID" ]}
elements={outsRows} elements={outsRows}
columnsWidth={[ 15, 65, 20 ]} columnsWidth={[ 15, 65, 20 ]}
@ -312,4 +312,4 @@ function Transaction() {
) )
} }
export default Transaction; export default Transaction;

View file

@ -1,10 +1,10 @@
import { useState } from "react"; import { useState } from "react";
import Header from "../../components/default/Header/Header"; import Header from "@/components/default/Header/Header";
import "../../styles/API.scss"; import styles from "@/styles/API.module.scss";
import APIItemValue from "../../interfaces/common/APIItemValue"; import APIItemValue from "@/interfaces/common/APIItemValue";
import examples from "./examples"; import examples from "./examples";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import JsonViewStyled from "../../components/UI/JsonViewStyled/JsonViewStyled"; import JsonViewStyled from "@/components/UI/JsonViewStyled/JsonViewStyled";
interface APIEndpointItemProps { interface APIEndpointItemProps {
title: string, title: string,
@ -92,14 +92,14 @@ function API() {
const { title, values, json } = props; const { title, values, json } = props;
return ( return (
<div className="api__item"> <div className={styles["api__item"]}>
<div className="api__item__title"> <div className={styles["api__item__title"]}>
<h3>{title}</h3> <h3>{title}</h3>
</div> </div>
<div className="api__item__units"> <div className={styles["api__item__units"]}>
{ {
values?.map(e => ( values?.map(e => (
<div key={e.key} className="api__item__unit"> <div key={e.key} className={styles["api__item__unit"]}>
<div> <div>
<p>{e.key}</p> <p>{e.key}</p>
</div> </div>
@ -111,7 +111,7 @@ function API() {
} }
</div> </div>
{json && {json &&
<div className="api__item__json"> <div className={styles["api__item__json"]}>
<p>JSON Response</p> <p>JSON Response</p>
<JsonViewStyled <JsonViewStyled
data={json} data={json}
@ -137,16 +137,16 @@ function API() {
} }
return ( return (
<div className="api"> <div className={styles["api"]}>
<Header <Header
page="API" page="API"
burgerOpened={burgerOpened} burgerOpened={burgerOpened}
setBurgerOpened={setBurgerOpened} setBurgerOpened={setBurgerOpened}
/> />
<div className="api__title"> <div className={styles["api__title"]}>
<p>API Documentation</p> <p>API Documentation</p>
</div> </div>
<div className="api__items"> <div className={styles["api__items"]}>
<APIItem title="How to use" values={howToUseValues} /> <APIItem title="How to use" values={howToUseValues} />
{ {
endpoints.map(e => ( endpoints.map(e => (

View file

@ -16,15 +16,22 @@
"module": "esnext", "module": "esnext",
"moduleResolution": "node", "moduleResolution": "node",
"resolveJsonModule": true, "resolveJsonModule": true,
// "isolatedModules": true,
"noEmit": true, "noEmit": true,
"jsx": "react-jsx" "jsx": "preserve",
"incremental": true,
"isolatedModules": true,
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
}, },
"include": [ "include": [
"src" "src"
], ],
"exclude": ["src/setupProxy.js"], "exclude": [
"src/setupProxy.js"
],
"ts-node": { "ts-node": {
"esm": true "esm": true
}, }
} }