diff --git a/package.json b/package.json index d82ca89f..ee63a3fd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "explorer-dapp", "description": "Itheum Explorer is a DApp for the public to explore and visualize data within the Itheum protocol", - "version": "1.21.1", + "version": "1.22.2", "author": "Itheum", "license": "GPL-3.0-or-later", "dependencies": { diff --git a/src/components/Layout/Navbar.tsx b/src/components/Layout/Navbar.tsx index 0f1b6a98..a9a345b9 100644 --- a/src/components/Layout/Navbar.tsx +++ b/src/components/Layout/Navbar.tsx @@ -97,6 +97,13 @@ export const Navbar = () => { Gamer Passport + + + AI Workforce + + { - const envKey = chainID === "1" ? "VITE_ENV_BACKEND_MAINNET_API" : "VITE_ENV_BACKEND_API"; - const defaultUrl = chainID === "1" ? "https://production-itheum-api.up.railway.app" : "https://staging-itheum-api.up.railway.app"; +export const backendApi = (chainID?: string) => { + if (!chainID) { + if (import.meta.env.VITE_ENV_NETWORK === "mainnet") { + return "https://production-itheum-api.up.railway.app"; + } else { + return "https://staging-itheum-api.up.railway.app"; + } + } else { + const envKey = chainID === "1" ? "VITE_ENV_BACKEND_MAINNET_API" : "VITE_ENV_BACKEND_API"; + const defaultUrl = chainID === "1" ? "https://production-itheum-api.up.railway.app" : "https://staging-itheum-api.up.railway.app"; - return import.meta.env[envKey] || defaultUrl; + return import.meta.env[envKey] || defaultUrl; + } }; export async function getHealthCheckFromBackendApi(chainID: string): Promise { diff --git a/src/libs/utils/constant.ts b/src/libs/utils/constant.ts index 543bc31c..740c6042 100644 --- a/src/libs/utils/constant.ts +++ b/src/libs/utils/constant.ts @@ -17,35 +17,35 @@ export const APP_MAPPINGS = [ appName: "Get Points", appDescription: "Collect Itheum Points", routeKey: "getbitz", - desc: "Open this app to get XP by playing a simple meme burning gaming. XP is a proof-of-activity system for the Itheum Protocol can swap to ITHEUM airdrops in future ;)", + desc: "Use this app to get XP by playing a simple meme burning game. XP is a proof-of-activity system for the Itheum Protocol.", img: iconGetBitz, }, { appName: "TrailBlazer", appDescription: "TrailBlazer Quest App", routeKey: "itheumtrailblazer", - desc: "Hardcore community members unlock Project Alpha by owning their favorite project's TrailBlazer Data NFTs. Unlock and visualize these TrailBlazer Data NFTs by using this app.", + desc: "Hardcore community members unlock Project Alpha by owning TrailBlazer Data NFTs that unlock private quests.", img: iconTrailblazer, }, { appName: "NF-Tunes", appDescription: "Listen to Music Playlists", routeKey: "nftunes", - desc: "Listen to music from indie musicians with this app. All music is unlocked from Music Data NFTs. Like what you hear? why not support the musician by owning a Music Data NFT.", + desc: "Listen to music from indie musicians with this app. Like what you hear? why not support the musician by owning a Music Data NFT.", img: iconNFTunes, }, { - appName: "Deep Forest Music Data NFT Player", + appName: "Deep Forest Music", appDescription: "The Chronicles of Deep Forest", routeKey: "deepforestmusic", - desc: "Experience Grammy Award winner Deep Forest's timeless music reimagined in a limited edition 90-piece Music Data NFT collection.", + desc: "Experience Grammy Award winner Deep Forest's timeless music re-imagined in a limited edition 90-piece Music Data NFT collection.", img: iconDeepForest, }, { appName: "Time Capsule", appDescription: "Preserve Memories Forever", routeKey: "timecapsule", - desc: "Capture, archive, and relive historic social media events through photos and videos, preserving memories for future generations. Join the nostalgia journey!", + desc: "Capture, archive, and relive historic events through photos and videos, preserving memories for future generations.", img: iconTimeCapsule, }, { @@ -66,35 +66,35 @@ export const APP_MAPPINGS = [ appName: "Bober Game Room", appDescription: "Play a fun Bober Video Game", routeKey: "bobergameroom", - desc: "Annoying memes are flooding the forest! Use your trusty water cannon to blast them away before they reach the dam! It's a beaver battle against cringe!", + desc: "Annoying memes are flooding the forest! Use your trusty water cannon to blast them away before they reach the dam!", img: iconBober, }, { appName: "MultiversX ESDT Bubbles", appDescription: "MultiversX ESDT Bubbles Data NFT", routeKey: "esdtBubble", - desc: "ESDT is the native token standard of the MultiversX blockchain. This app visualizes the dynamic data stream of various ESDT token insights as bubble graphs.", + desc: "This app visualizes the dynamic data stream of various ESDT token insights as bubble graphs.", img: "https://itheum-static.s3.ap-southeast-2.amazonaws.com/expl-app-esdt-bubbles-icon.png", }, { appName: "MultiversX Bubbles", appDescription: "MultiversX Datasets Visualized", routeKey: "multiversxbubbles", - desc: "This app visualizes dynamic data streams of various MultiversX ecosystem metrics and insights as bubble graphs. Get MultiversX ecosystem alpha today!", + desc: "This app visualizes dynamic data streams of various MultiversX ecosystem metrics and insights as bubble graphs.", img: iconBubbleMaps, }, { appName: "PlayStation Gamer Passport", appDescription: "PlayStation Gamer Passport Data NFT", routeKey: "playstationgamerpassport", - desc: "There are over 110 million active Sony Playstation gamers, and now, they can own some of their data. Unlock these PlatStation gamers Data NFTs by using this app.", + desc: "There are over 110 million active Sony Playstation gamers, and now, they can own some of their data.", img: "https://itheum-static.s3.ap-southeast-2.amazonaws.com/expl-app-playstation-icon.png", }, { appName: "MultiversX Infographics", appDescription: "View Dynamic Infographic PDFs", routeKey: "multiversxinfographics", - desc: 'This app visualizes dynamic and evolving data streams rendered into PDF files that showcase unique MultiversX ecosystem "alpha", insights, and education.', + desc: "This app visualizes dynamic and evolving data streams rendered into PDF files.", img: iconInfrographics, }, ]; diff --git a/src/pages/AIWorkforce/AIWorkforce.tsx b/src/pages/AIWorkforce/AIWorkforce.tsx new file mode 100644 index 00000000..4cfa12da --- /dev/null +++ b/src/pages/AIWorkforce/AIWorkforce.tsx @@ -0,0 +1,213 @@ +import React, { useEffect, useState } from "react"; +import axios from "axios"; +import { Link } from "react-router-dom"; +import { Button } from "libComponents/Button"; +import { backendApi } from "libs/backend-api"; +import { WorkersSnapShotGrid } from "./SharedComps"; + +// const rankedWorkforce = [ +// { +// "uuid": 358, +// "address": "erd1eweqykxcrhh5nps4fzktu9eftf7rxpa8xdfkypwz0k4huhl0s04sa6tqyd", +// "rankScore": 10, +// "bondAmount": 566, +// "livelinessScore": 1, +// "bitzXp": 0, +// "vault": "DATANFTFT-e0b917-024b", +// "vaultImg": "https://devnet-media.elrond.com/nfts/asset/QmUHzpCRRLguzjAEnXB33Ar2FbR4EE18KRCChjUPyLyCUo", +// "creationTime": "2024-09-02T08:18:32.000Z", +// "lastUpdated": 1725277510, +// }, +// { +// "uuid": 359, +// "address": "erd1rzjsadakpqfqm2umcv96d7d95zcwzm7pac8g4kmcp6kdfgzmuhgs2kyrzv", +// "rankScore": 0, +// "bondAmount": 1001, +// "livelinessScore": 0, +// "bitzXp": 0, +// "vault": "DATANFTFT-e0b917-0244", +// "vaultImg": "https://devnet-media.elrond.com/nfts/asset/QmWxrgB4o6QTJUsbCmZXSSPc3WvFvh4RBrLs9pwJSpEGUw", +// "creationTime": "2024-08-27T07:13:14.000Z", +// "lastUpdated": 1725277508, +// }, +// { +// "uuid": 360, +// "address": "erd1rzjsadakpqfqm2umcv96d7d95zcwzm7pac8g4kmcp6kdfgzmuhgs2kyrzv", +// "rankScore": 0, +// "bondAmount": 1001, +// "livelinessScore": 0, +// "bitzXp": 0, +// "vault": "DATANFTFT-e0b917-0249", +// "vaultImg": "https://devnet-media.elrond.com/nfts/asset/QmRicYzifHSQP9xk6FaA7QqnRQN3qgd4WqLhqGxrJJqto9", +// "creationTime": "2024-08-27T07:23:56.000Z", +// "lastUpdated": 1725277509, +// }, +// { +// "uuid": 361, +// "address": "erd1mywl5r9ptxurzhnz2ztrjdjswssd44l05n3l5ly3795gf777ujmqma76gx", +// "rankScore": 0, +// "bondAmount": 811, +// "livelinessScore": 0, +// "bitzXp": 0, +// "vault": "DATANFTFT-e0b917-0242", +// "vaultImg": "https://devnet-media.elrond.com/nfts/asset/QmWDyB3mQMEgJ2cfNS2yWf8AmZ7CRs52eDmvtqqgELuLnK", +// "creationTime": "2024-08-27T07:05:56.000Z", +// "lastUpdated": 1725277507, +// }, +// { +// "uuid": 362, +// "address": "erd1cyp3c6plhrmlwzue7f7cg8kqs6jf0c6temmh6gsh3tfj3de3a7csddfm6t", +// "rankScore": 0, +// "bondAmount": 722, +// "livelinessScore": 0, +// "bitzXp": 0, +// "vault": "DATANFTFT-e0b917-0248", +// "vaultImg": "https://devnet-media.elrond.com/nfts/asset/QmPgQ3brZgf8izBNYKAERiaKDnmxeGhUZQYXhCLJ2kqvFV", +// "creationTime": "2024-08-27T07:20:44.000Z", +// "lastUpdated": 1725277509, +// }, +// { +// "uuid": 363, +// "address": "erd1cyp3c6plhrmlwzue7f7cg8kqs6jf0c6temmh6gsh3tfj3de3a7csddfm6t", +// "rankScore": 0, +// "bondAmount": 722, +// "livelinessScore": 0, +// "bitzXp": 0, +// "vault": "DATANFTFT-e0b917-024a", +// "vaultImg": "https://devnet-media.elrond.com/nfts/asset/QmRSRLEcPJ2hgeAJ6bey83AWgg3QVjojVwi5CCYLj7R82h", +// "creationTime": "2024-08-27T07:58:32.000Z", +// "lastUpdated": 1725277509, +// }, +// { +// "uuid": 364, +// "address": "erd1fet6nrput6ujj0yf4cj70ex0sldxsf79ruxsucapeuf5k4acmldqwkkgp2", +// "rankScore": 0, +// "bondAmount": 522, +// "livelinessScore": 0, +// "bitzXp": 0, +// "vault": "DATANFTFT-e0b917-023b", +// "vaultImg": "https://devnet-media.elrond.com/nfts/asset/QmNXaJen63E65Qvzc2j4guZ3chUmUUJwtfsyj31gk9317i", +// "creationTime": "2024-08-23T04:43:32.000Z", +// "lastUpdated": 1725277504, +// }, +// { +// "uuid": 365, +// "address": "erd1fj4v79xn96ew4fu05e9m2hxzuw0d03txsj7ptn2ls0pa7y56hhksexjhxj", +// "rankScore": 0, +// "bondAmount": 380, +// "livelinessScore": 0, +// "bitzXp": 0, +// "vault": "DATANFTFT-e0b917-0243", +// "vaultImg": "https://devnet-media.elrond.com/nfts/asset/QmVBAHqgRCSWi6ZAH1D4bMBBmsfvsiXLMdngNZQCSTKwb1", +// "creationTime": "2024-08-27T07:09:44.000Z", +// "lastUpdated": 1725277507, +// }, +// { +// "uuid": 366, +// "address": "erd1fj4v79xn96ew4fu05e9m2hxzuw0d03txsj7ptn2ls0pa7y56hhksexjhxj", +// "rankScore": 0, +// "bondAmount": 380, +// "livelinessScore": 0, +// "bitzXp": 0, +// "vault": "DATANFTFT-e0b917-0245", +// "vaultImg": "https://devnet-media.elrond.com/nfts/asset/QmVGN1ptYoccudhB66jKvs6puFyaWJp9mmB2MybtEM1nf9", +// "creationTime": "2024-08-27T07:16:32.000Z", +// "lastUpdated": 1725277509, +// }, +// { +// "uuid": 367, +// "address": "erd1yz6vqzlv6zw70u2rq5sdljnq59tn2ue879xxrfpyfts4wy09rd0qpnctn8", +// "rankScore": 0, +// "bondAmount": 299, +// "livelinessScore": 0, +// "bitzXp": 0, +// "vault": "DATANFTFT-e0b917-0234", +// "vaultImg": "https://devnet-media.elrond.com/nfts/asset/QmNkuz9Xx9pCibCFxaDwZvnd3KbxYQ7HTWruzpQwonJwsf", +// "creationTime": "2024-08-22T10:36:26.000Z", +// "lastUpdated": 1725277500, +// }, +// ]; + +export const AIWorkforce = () => { + const [appBootingUp, setAppBootingUp] = useState(true); + const [rankedWorkforce, setRankedWorkforce] = useState([]); + + useEffect(() => { + window.scrollTo({ + top: 0, + behavior: "smooth", + }); + + async function getDataAndInitGraphData() { + setAppBootingUp(true); + const workforceDataList = await getWorkforceData(); + setRankedWorkforce(workforceDataList); + setAppBootingUp(false); + } + + getDataAndInitGraphData(); + }, []); + + async function getWorkforceData() { + const apiResponse = await axios.get(`${backendApi()}/workforce?size=50`); + const dataResponse = apiResponse.data; + + return dataResponse; + } + + return ( + + + + + Join the Itheum AI Workforce + + Mint Your NFMe ID, Boost Your Liveliness, Farm Staking Rewards and Join the Itheum AI Workforce as a Data Provider + + + + + + Mint NFMe ID Now + + + + + + + {appBootingUp ? <>Loading> : } + + + ); +}; + +export const AIWorkforceTopN = ({ showItems }: { showItems?: number }) => { + const [appBootingUp, setAppBootingUp] = useState(true); + const [rankedWorkforce, setRankedWorkforce] = useState([]); + + useEffect(() => { + async function getDataAndInitGraphData() { + setAppBootingUp(true); + const workforceDataList = await getWorkforceData(); + setRankedWorkforce(workforceDataList); + setAppBootingUp(false); + } + + getDataAndInitGraphData(); + }, []); + + async function getWorkforceData() { + const apiResponse = await axios.get(`${backendApi()}/workforce?size=${showItems || 5}`); + const dataResponse = apiResponse.data; + + return dataResponse; + } + + return ( + + + {appBootingUp ? <>Loading> : } + + + ); +}; diff --git a/src/pages/AIWorkforce/SharedComps.tsx b/src/pages/AIWorkforce/SharedComps.tsx new file mode 100644 index 00000000..264ad297 --- /dev/null +++ b/src/pages/AIWorkforce/SharedComps.tsx @@ -0,0 +1,37 @@ +import React from "react"; + +export function WorkersSnapShotGrid({ snapShotData }: { snapShotData: any[] }) { + const flattenedFixedData: any[] = []; + + if (snapShotData) { + snapShotData.map((worker: any) => { + flattenedFixedData.push(worker); + }); + } + + return ( + + {flattenedFixedData && + flattenedFixedData.length > 0 && + flattenedFixedData.map((worker: any, idx: number) => ( + + + + Rank: {idx + 1} + + + + + + {worker.vault} + Rank Score: {worker.rankScore?.toLocaleString()} + Total Bond: {worker.bondAmount?.toLocaleString()} + Liveliness Score: {worker.livelinessScore?.toLocaleString()} + BiTz XP: {worker.bitzXp?.toLocaleString()} + + + + ))} + + ); +} diff --git a/src/pages/Analytics/AnalyticsPage.tsx b/src/pages/Analytics/AnalyticsPage.tsx index c06c9f93..7ae9706d 100644 --- a/src/pages/Analytics/AnalyticsPage.tsx +++ b/src/pages/Analytics/AnalyticsPage.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; import { CartesianGrid, Legend, Tooltip, ResponsiveContainer, XAxis, YAxis, AreaChart, Area } from "recharts"; -import { LoadingGraph, getAggregatedAnalyticsData, normalizeDataForMarshalUsage } from "./AnalyticsShared"; +import { LoadingGraph, getAggregatedAnalyticsData, normalizeDataForMarshalUsage, formatYAxisDate } from "./AnalyticsShared"; import { HeaderComponent } from "../../components/Layout/HeaderComponent"; export const AnalyticsPage = () => { @@ -104,7 +104,7 @@ export const AnalyticsPage = () => { - + { - + { - + { {(dataLakeUserGrowthData.length > 0 && ( - + [value, "Users"]} wrapperStyle={{ color: "#333" }} /> @@ -350,7 +350,7 @@ export const AnalyticsPage = () => { {(dataLakeDataVolumeGrowthData.length > 0 && ( - + [value.toLocaleString(), "Data Collected (in Bytes)"]} wrapperStyle={{ color: "#333" }} /> diff --git a/src/pages/Analytics/AnalyticsShared.tsx b/src/pages/Analytics/AnalyticsShared.tsx index 29d98560..fc9d160e 100644 --- a/src/pages/Analytics/AnalyticsShared.tsx +++ b/src/pages/Analytics/AnalyticsShared.tsx @@ -1,4 +1,5 @@ import axios from "axios"; +import moment from "moment-timezone"; import { getApiWeb2Apps } from "libs/utils"; export const LoadingGraph = () => { @@ -41,3 +42,7 @@ export function normalizeDataForMarshalUsage(dayData: any, day: string) { return chainMarshalUsageDataI; } + +export function formatYAxisDate(tickItem: any) { + return moment(tickItem).format("MM-DD-YY"); +} diff --git a/src/pages/Dashboard.tsx b/src/pages/Dashboard.tsx index 7e86adfb..5b81bdbf 100644 --- a/src/pages/Dashboard.tsx +++ b/src/pages/Dashboard.tsx @@ -12,11 +12,16 @@ export const Dashboard = () => { network: { explorerAddress }, } = useGetNetworkConfig(); const { hasPendingTransactions } = useGetPendingTransactions(); - const [scAddress, setScAddress] = useState(""); const [marketRequirements, setMarketRequirements] = useState(); const [isLoading, setIsLoading] = useState(true); + useEffect(() => { + if (!hasPendingTransactions) { + fetchMarketplaceRequirements(); + } + }, [hasPendingTransactions]); + async function fetchMarketplaceRequirements() { setIsLoading(true); @@ -28,12 +33,6 @@ export const Dashboard = () => { setIsLoading(false); } - useEffect(() => { - if (!hasPendingTransactions) { - fetchMarketplaceRequirements(); - } - }, [hasPendingTransactions]); - if (isLoading) { return ; } diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 8148cb99..72ade2b1 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -8,6 +8,7 @@ import TrendingSection from "components/TrendingSection"; import { APP_MAPPINGS } from "libs/utils/constant"; import { routeNames } from "routes"; import { Button } from "../libComponents/Button"; +import { AIWorkforceTopN } from "../pages/AIWorkforce/AIWorkforce"; import { AnalyticsSnapshot } from "../pages/Analytics/AnalyticsSnapshot"; export function returnRoute(routeKey: string) { @@ -50,7 +51,7 @@ export const Home = () => { - + Featured Apps @@ -80,7 +81,27 @@ export const Home = () => { - + + + + Top 5 Ranked Itheum AI Workforce + + + + + + + + + View All + + + + + + + + Protocol Activity @@ -100,7 +121,7 @@ export const Home = () => { - + All Apps @@ -130,14 +151,14 @@ export const Home = () => { - + - + ); }; diff --git a/src/pages/Unlock/index.tsx b/src/pages/Unlock/index.tsx index 405a8059..c92a0899 100644 --- a/src/pages/Unlock/index.tsx +++ b/src/pages/Unlock/index.tsx @@ -97,11 +97,6 @@ const UnlockPage = () => { - > )} diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 5b381384..dd2393d2 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -4,6 +4,7 @@ export * from "./NotFound"; export * from "./Unlock"; export * from "./Analytics/AnalyticsPage"; export * from "./GamerPassport/GamerPassport"; +export * from "./AIWorkforce/AIWorkforce"; //Widgets export * from "./AppMarketplace/MultiversxBubbles/MultiversxBubbles"; export * from "./AppMarketplace/MultiversxInfographics"; diff --git a/src/routes.ts b/src/routes.ts index cb376df9..4349c6fb 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -8,7 +8,7 @@ import { NFTunes } from "pages/AppMarketplace/NFTunes"; import { SpreadsheetNfts } from "pages/AppMarketplace/SpreadsheetNfts"; import { TimeCapsule } from "pages/AppMarketplace/TimeCapsule/TimeCapsule"; import { withPageTitle } from "./components/PageTitle"; -import { Home, MyListed, MyWallet, MultiversxBubbles, MultiversxInfographics, BoberGameRoom, AnalyticsPage, GamerPassport } from "./pages"; +import { Home, MyListed, MyWallet, MultiversxBubbles, MultiversxInfographics, BoberGameRoom, AnalyticsPage, GamerPassport, AIWorkforce } from "./pages"; export const routeNames = { home: "/", @@ -29,6 +29,7 @@ export const routeNames = { spreadsheetnfts: "/spreadsheetnfts", nfpodcast: "/nfpodcast", gamerpassport: "/gamerpassport", + aiworkforce: "/aiworkforce", }; interface RouteWithTitleType extends RouteType { @@ -123,6 +124,11 @@ export const routes: RouteWithTitleType[] = [ title: "Gamer Passport", component: GamerPassport, }, + { + path: routeNames.aiworkforce, + title: "AI Workforce", + component: AIWorkforce, + }, ]; export const mappedRoutes = routes.map((route) => {
{worker.vault}
Rank Score: {worker.rankScore?.toLocaleString()}
Total Bond: {worker.bondAmount?.toLocaleString()}
Liveliness Score: {worker.livelinessScore?.toLocaleString()}
BiTz XP: {worker.bitzXp?.toLocaleString()}