diff --git a/apps/stats-web/.dockerignore b/apps/stats-web/.dockerignore index 72e9aa4250..2cd409ba16 100644 --- a/apps/stats-web/.dockerignore +++ b/apps/stats-web/.dockerignore @@ -3,5 +3,5 @@ Dockerfile node_modules npm-debug.log README.md -.next +dist .git \ No newline at end of file diff --git a/apps/stats-web/.env.local.sample b/apps/stats-web/.env.local.sample index 6d828e629d..b37d397bcd 100644 --- a/apps/stats-web/.env.local.sample +++ b/apps/stats-web/.env.local.sample @@ -1,18 +1,18 @@ DEPLOYMENT_ENV=staging -NEXT_PUBLIC_API_BASE_URL=http://localhost:3080 -NEXT_PUBLIC_NODE_ENV=$NODE_ENV -NEXT_PUBLIC_BASE_API_MAINNET_URL=$NEXT_PUBLIC_API_BASE_URL -NEXT_PUBLIC_BASE_API_TESTNET_URL=$NEXT_PUBLIC_API_BASE_URL -NEXT_PUBLIC_BASE_API_SANDBOX_URL=$NEXT_PUBLIC_API_BASE_URL -NEXT_PUBLIC_DEFAULT_NETWORK_ID=mainnet +VITE_API_BASE_URL=http://localhost:3080 +VITE_BASE_API_MAINNET_URL=$VITE_API_BASE_URL +VITE_BASE_API_SANDBOX_URL=$VITE_API_BASE_URL +VITE_BASE_API_TESTNET_URL=$VITE_API_BASE_URL +VITE_DEFAULT_NETWORK_ID=mainnet +VITE_NODE_ENV=$NODE_ENV API_HOST_WITH_DEFAULT=${API_HOST:-localhost} BASE_API_MAINNET_URL=http://${API_HOST_WITH_DEFAULT}:3080 -BASE_API_TESTNET_URL=${BASE_API_MAINNET_URL} BASE_API_SANDBOX_URL=${BASE_API_MAINNET_URL} +BASE_API_TESTNET_URL=${BASE_API_MAINNET_URL} LOG_LEVEL=debug -NEXT_PUBLIC_LOG_LEVEL=debug -NEXT_PUBLIC_SENTRY_ENABLED=false +VITE_LOG_LEVEL=debug +VITE_SENTRY_ENABLED=false diff --git a/apps/stats-web/.eslintrc.cjs b/apps/stats-web/.eslintrc.cjs new file mode 100644 index 0000000000..9b50f364e9 --- /dev/null +++ b/apps/stats-web/.eslintrc.cjs @@ -0,0 +1,16 @@ +module.exports = { + extends: [require.resolve("@akashnetwork/dev-config/.eslintrc.ts")], + overrides: [ + { + files: ["*.ts", "*.tsx"], + rules: { + "react/no-unescaped-entities": "warn", + "react-hooks/exhaustive-deps": "warn", + "react/display-name": "off" + } + } + ], + settings: { + "import-x/ignore": ["react"] + } +}; diff --git a/apps/stats-web/.eslintrc.js b/apps/stats-web/.eslintrc.js deleted file mode 100644 index 1e39767119..0000000000 --- a/apps/stats-web/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require("@akashnetwork/dev-config/.eslintrc.next"); diff --git a/apps/stats-web/.eslintrc.json b/apps/stats-web/.eslintrc.json deleted file mode 100644 index bffb357a71..0000000000 --- a/apps/stats-web/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "next/core-web-vitals" -} diff --git a/apps/stats-web/.gitignore b/apps/stats-web/.gitignore index 47fa2a149e..b386039ec4 100644 --- a/apps/stats-web/.gitignore +++ b/apps/stats-web/.gitignore @@ -9,13 +9,9 @@ # testing /coverage -# next.js -/.next/ -/out/ - # production +/dist /build -env-config.schema.js # misc .DS_Store @@ -29,9 +25,5 @@ yarn-error.log* # local env files .env*.local -# vercel -.vercel - # typescript *.tsbuildinfo -next-env.d.ts diff --git a/apps/stats-web/.prettierrc.js b/apps/stats-web/.prettierrc.cjs similarity index 100% rename from apps/stats-web/.prettierrc.js rename to apps/stats-web/.prettierrc.cjs diff --git a/apps/stats-web/deploy.yml b/apps/stats-web/deploy.yml index 362083926b..1ac18ff885 100644 --- a/apps/stats-web/deploy.yml +++ b/apps/stats-web/deploy.yml @@ -6,9 +6,8 @@ services: image: ghcr.io/akash-network/stats-web: # env: # - API_BASE_URL= -# - NEXT_PUBLIC_GA_MEASUREMENT_ID= -# - NEXT_PUBLIC_SENTRY_DSN= -# - NEXT_PUBLIC_SENTRY_SERVER_NAME= +# - VITE_GA_MEASUREMENT_ID= +# - VITE_SENTRY_DSN= expose: - port: 3000 as: 80 diff --git a/apps/stats-web/env/.env b/apps/stats-web/env/.env index b930940ceb..41e65f6a14 100644 --- a/apps/stats-web/env/.env +++ b/apps/stats-web/env/.env @@ -1,7 +1,7 @@ -NEXT_PUBLIC_UNLEASH_APP_NAME=stats-web -NEXT_PUBLIC_UNLEASH_ENABLE_ALL=false +VITE_UNLEASH_ENABLE_ALL=false + +VITE_SENTRY_DSN="https://c2af143d11c20d4bf20e7b668d493aaf@o877251.ingest.us.sentry.io/4510740211105792" +VITE_SENTRY_ENABLED=false -NEXT_PUBLIC_SENTRY_APPLICATION_KEY="AKASH-STATS-WEB" -NEXT_PUBLIC_SENTRY_DSN="https://c2af143d11c20d4bf20e7b668d493aaf@o877251.ingest.us.sentry.io/4510740211105792" SENTRY_ORG=cloudmos -SENTRY_PROJECT=$NEXT_PUBLIC_UNLEASH_APP_NAME +SENTRY_PROJECT=stats-web diff --git a/apps/stats-web/env/.env.production b/apps/stats-web/env/.env.production index 286b7740a0..b1e403cd7a 100644 --- a/apps/stats-web/env/.env.production +++ b/apps/stats-web/env/.env.production @@ -1,16 +1,9 @@ -NEXT_PUBLIC_NODE_ENV=$NODE_ENV -NEXT_PUBLIC_BASE_API_MAINNET_URL=https://console-api.akash.network -NEXT_PUBLIC_BASE_API_SANDBOX_URL=https://console-api-sandbox.akash.network -NEXT_PUBLIC_BASE_API_TESTNET_URL=https://console-api-testnet.akash.network -NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_BASE_API_MAINNET_URL +VITE_API_BASE_URL=$VITE_BASE_API_MAINNET_URL +VITE_BASE_API_MAINNET_URL=https://console-api.akash.network +VITE_BASE_API_SANDBOX_URL=https://console-api-sandbox.akash.network +VITE_BASE_API_TESTNET_URL=https://console-api-testnet.akash.network +VITE_NODE_ENV=$NODE_ENV -NEXT_PUBLIC_GA_MEASUREMENT_ID=G-MLJ2W9K3MJ +VITE_GA_MEASUREMENT_ID=G-MLJ2W9K3MJ -BASE_API_MAINNET_URL=$NEXT_PUBLIC_BASE_API_MAINNET_URL -BASE_API_TESTNET_URL=$NEXT_PUBLIC_BASE_API_TESTNET_URL -BASE_API_SANDBOX_URL=$NEXT_PUBLIC_BASE_API_SANDBOX_URL - -NEXT_PUBLIC_UNLEASH_FRONTEND_API_URL=https://features-edge.akash.network/api/frontend -NEXT_PUBLIC_UNLEASH_FRONTEND_API_TOKEN=*:production.3de2776029292b1860a520b1aa3ee9e417ae300283811fd77d231060 - -NEXT_PUBLIC_SENTRY_ENABLED="true" +VITE_SENTRY_ENABLED=true diff --git a/apps/stats-web/env/.env.sample b/apps/stats-web/env/.env.sample index be76e73497..b7e988c027 100644 --- a/apps/stats-web/env/.env.sample +++ b/apps/stats-web/env/.env.sample @@ -1,8 +1,10 @@ -NEXT_PUBLIC_API_BASE_URL= -NEXT_PUBLIC_UNLEASH_ENABLE_ALL=true -NEXT_PUBLIC_UNLEASH_FRONTEND_API_URL= -NEXT_PUBLIC_UNLEASH_FRONTEND_API_TOKEN= +VITE_API_BASE_URL= +VITE_BASE_API_MAINNET_URL= +VITE_BASE_API_SANDBOX_URL= +VITE_BASE_API_TESTNET_URL= +VITE_UNLEASH_ENABLE_ALL=true -NEXT_PUBLIC_SENTRY_APPLICATION_KEY= -NEXT_PUBLIC_SENTRY_DSN= -NEXT_PUBLIC_SENTRY_SERVER_NAME= +VITE_GA_MEASUREMENT_ID= + +VITE_SENTRY_DSN= +VITE_SENTRY_ENABLED=false diff --git a/apps/stats-web/env/.env.staging b/apps/stats-web/env/.env.staging index dd0009eb14..09a45decf6 100644 --- a/apps/stats-web/env/.env.staging +++ b/apps/stats-web/env/.env.staging @@ -1,14 +1,7 @@ -NEXT_PUBLIC_NODE_ENV=$NODE_ENV -NEXT_PUBLIC_BASE_API_MAINNET_URL=https://console-api-mainnet-staging.akash.network -NEXT_PUBLIC_BASE_API_SANDBOX_URL=https://console-api-sandbox-staging.akash.network -NEXT_PUBLIC_BASE_API_TESTNET_URL=$NEXT_PUBLIC_BASE_API_MAINNET_URL -NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_BASE_API_MAINNET_URL +VITE_API_BASE_URL=$VITE_BASE_API_MAINNET_URL +VITE_BASE_API_MAINNET_URL=https://console-api-mainnet-staging.akash.network +VITE_BASE_API_SANDBOX_URL=https://console-api-sandbox-staging.akash.network +VITE_BASE_API_TESTNET_URL=$VITE_BASE_API_MAINNET_URL +VITE_NODE_ENV=$NODE_ENV -BASE_API_MAINNET_URL=$NEXT_PUBLIC_BASE_API_MAINNET_URL -BASE_API_TESTNET_URL=$NEXT_PUBLIC_BASE_API_TESTNET_URL -BASE_API_SANDBOX_URL=$NEXT_PUBLIC_BASE_API_SANDBOX_URL - -NEXT_PUBLIC_UNLEASH_FRONTEND_API_URL=https://features-edge-beta.akash.network/api/frontend -NEXT_PUBLIC_UNLEASH_FRONTEND_API_TOKEN=*:development.34efccc4c8ba7d6ddeeb4083d72bc8ac05770774ac4d6668e749ca5a - -NEXT_PUBLIC_SENTRY_ENABLED="true" +VITE_SENTRY_ENABLED=true diff --git a/apps/stats-web/index.html b/apps/stats-web/index.html new file mode 100644 index 0000000000..efba866337 --- /dev/null +++ b/apps/stats-web/index.html @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + Akash Network Stats + + +
+ + + diff --git a/apps/stats-web/next.config.js b/apps/stats-web/next.config.js deleted file mode 100644 index 2e5055bad7..0000000000 --- a/apps/stats-web/next.config.js +++ /dev/null @@ -1,81 +0,0 @@ -require("@akashnetwork/env-loader"); -const { version, repository } = require("./package.json"); -const { withSentryConfig } = require("@sentry/nextjs"); - -try { - const { browserEnvSchema } = require("./env-config.schema"); - - browserEnvSchema.parse(process.env); -} catch (error) { - if (error.message.includes("Cannot find module")) { - console.warn("No env-config.schema.js found, skipping env validation"); - } -} - -/** @type {import('next').NextConfig} */ -const nextConfig = { - output: "standalone", - env: { - NEXT_PUBLIC_APP_VERSION: version - }, - eslint: { - ignoreDuringBuilds: true - }, - transpilePackages: ["geist", "@akashnetwork/ui"], - experimental: { - instrumentationHook: true - }, - /** - * - * @param {import('webpack').Configuration} config - * @returns - */ - webpack: config => { - config.externals.push("pino-pretty"); - - return config; - } -}; - -/** - * For all available options, see: - * https://github.com/getsentry/sentry-webpack-plugin#options. - * @type {import('@sentry/nextjs').SentryBuildOptions} - */ -const sentryWebpackPluginOptions = { - // Additional config options for the Sentry Webpack plugin. Keep in mind that - // the following options are set automatically, and overriding them is not - // recommended: - // release, url, org, project, authToken, configFile, stripPrefix, - // urlPrefix, include, ignore - - // silent: !process.env.CI, // Suppresses all logs, - // dryRun: true, - authToken: process.env.SENTRY_AUTH_TOKEN, - org: process.env.SENTRY_ORG, - project: process.env.SENTRY_PROJECT, - release: repository - ? { - name: version, - setCommits: { - repo: new URL(repository.url).pathname.slice(1).replace(/\.git$/, ""), - commit: process.env.GIT_COMMIT_HASH - } - } - : { name: version }, - sourcemaps: { - deleteSourcemapsAfterUpload: false - }, - widenClientFileUpload: true, - debug: !process.env.CI, - reactComponentAnnotation: { - enabled: true - }, - unstable_sentryWebpackPluginOptions: { - applicationKey: process.env.NEXT_PUBLIC_SENTRY_APPLICATION_KEY - } -}; - -// Make sure adding Sentry options is the last code to run before exporting, to -// ensure that your source maps include changes from all other Webpack plugins -module.exports = sentryWebpackPluginOptions.authToken ? withSentryConfig(nextConfig, sentryWebpackPluginOptions) : nextConfig; diff --git a/apps/stats-web/nginx.conf b/apps/stats-web/nginx.conf new file mode 100644 index 0000000000..e5ee4aea21 --- /dev/null +++ b/apps/stats-web/nginx.conf @@ -0,0 +1,26 @@ +server { + listen 3000; + server_name _; + server_tokens off; + + root /usr/share/nginx/html; + index index.html; + + # gzip compression + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml; + + # Cache static assets + location /assets/ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # SPA fallback - serve index.html for all non-file routes + location / { + try_files $uri $uri/ /index.html; + } +} diff --git a/apps/stats-web/package.json b/apps/stats-web/package.json index 6af07203ab..822dea8be1 100644 --- a/apps/stats-web/package.json +++ b/apps/stats-web/package.json @@ -2,19 +2,22 @@ "name": "@akashnetwork/stats-web", "version": "1.14.1", "private": true, + "type": "module", "scripts": { - "build": "npm run build-env-schemas && next build", + "build": "vite build", + "typecheck": "tsc --noEmit", "build:container": "dc build stats-web --build-arg DEPLOYMENT_ENV=production", - "build-env-schemas": "tsc src/config/env-config.schema.ts --outDir . --skipLibCheck --target es2023 --moduleResolution node", - "dev": "next dev", - "dev:inspect": "cross-env NODE_OPTIONS=\"--inspect\" ../../node_modules/.bin/next start -p 3001", + "dev": "vite", "format": "prettier --write ./*.{ts,js,json} **/*.{ts,tsx,js,json}", "lint": "eslint .", - "start": "next start" + "preview": "vite preview", + "start": "vite preview", + "test": "npm run test:unit", + "test:unit": "vitest run", + "test:unit:watch": "vitest" }, "dependencies": { "@akashnetwork/chain-sdk": "1.0.0-alpha.24", - "@akashnetwork/env-loader": "*", "@akashnetwork/logging": "*", "@akashnetwork/network-store": "*", "@akashnetwork/ui": "*", @@ -24,11 +27,11 @@ "@nivo/line": "^0.87.0", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-slot": "^1.0.2", - "@sentry/nextjs": "^9.37.0", + "@sentry/react": "^9.37.0", "@tanstack/react-query": "^5.67.2", "@tanstack/react-table": "^8.11.2", "@textea/json-viewer": "^3.2.3", - "@unleash/nextjs": "^1.6.2", + "@unleash/proxy-client-react": "^4.3.2", "axios": "^1.7.2", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", @@ -38,19 +41,20 @@ "jotai": "^2.5.1", "lightweight-charts": "^4.2.0", "lucide-react": "^0.292.0", - "next": "^14.2.35", - "next-nprogress-bar": "^2.1.2", - "next-qrcode": "^2.5.1", - "next-themes": "^0.2.1", - "nextjs-google-analytics": "^2.3.3", + "next-themes": "^0.4.4", + "nprogress": "^0.2.0", "react": "18.2.0", "react-dom": "18.2.0", + "react-ga4": "^2.1.0", + "react-helmet-async": "^2.0.5", "react-icons": "^5.0.1", "react-intl": "^6.5.5", "react-modern-drawer": "^1.2.2", - "sharp": "^0.34.5", + "react-qrcode-logo": "^3.0.0", + "react-router-dom": "^6.28.0", "tailwind-merge": "^2.0.0", "usehooks-ts": "^2.9.1", + "web-vitals": "^4.2.4", "zod": "^3.22.4" }, "devDependencies": { @@ -58,20 +62,28 @@ "@akashnetwork/docker": "*", "@akashnetwork/net": "*", "@akashnetwork/releaser": "*", + "@testing-library/dom": "^10.4.0", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/react": "^16.2.0", + "@testing-library/user-event": "^14.6.1", "@types/json2csv": "^5.0.7", "@types/node": "^22.13.11", + "@types/nprogress": "^0.2.3", "@types/react": "18.2.0", "@types/react-dom": "18.2.0", "@typescript-eslint/eslint-plugin": "^7.12.0", + "@vitejs/plugin-react": "^4.3.4", "autoprefixer": "^10.4.16", "eslint": "^8.57.0", - "eslint-config-next": "^14.2.3", "eslint-plugin-simple-import-sort": "^12.1.0", + "jsdom": "^28.1.0", "postcss": "^8.4.31", "postcss-nesting": "^12.0.1", "prettier": "^3.3.0", "prettier-plugin-tailwindcss": "^0.6.1", "tailwindcss": "^3.3.5", - "typescript": "~5.8.2" + "typescript": "~5.8.2", + "vite": "^6.0.7", + "vitest": "^4.0.0" } } diff --git a/apps/stats-web/postcss.config.js b/apps/stats-web/postcss.config.cjs similarity index 100% rename from apps/stats-web/postcss.config.js rename to apps/stats-web/postcss.config.cjs diff --git a/apps/stats-web/public/fonts/Geist-Variable.woff2 b/apps/stats-web/public/fonts/Geist-Variable.woff2 new file mode 100644 index 0000000000..9983e92a03 Binary files /dev/null and b/apps/stats-web/public/fonts/Geist-Variable.woff2 differ diff --git a/apps/stats-web/sentry.client.config.ts b/apps/stats-web/sentry.client.config.ts index c302909604..5cf7dde628 100644 --- a/apps/stats-web/sentry.client.config.ts +++ b/apps/stats-web/sentry.client.config.ts @@ -1,25 +1,23 @@ // This file configures the initialization of Sentry on the browser. // The config you add here will be used whenever a page is visited. -// https://docs.sentry.io/platforms/javascript/guides/nextjs/ +// https://docs.sentry.io/platforms/javascript/guides/react/ -import { inboundFiltersIntegration, init as initSentry, thirdPartyErrorFilterIntegration } from "@sentry/nextjs"; +import { browserTracingIntegration, inboundFiltersIntegration, init as initSentry, thirdPartyErrorFilterIntegration } from "@sentry/react"; initSentry({ - dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, + dsn: import.meta.env.VITE_SENTRY_DSN, // Adjust this value in production, or use tracesSampler for greater control tracesSampleRate: 0.1, - enabled: process.env.NEXT_PUBLIC_SENTRY_ENABLED === "true", - // propagate sentry-trace and baggage headers to internal API only - // everything else will be done with custom interceptor - tracePropagationTargets: [/^\/api\//, /^\/_next\//], + enabled: import.meta.env.VITE_SENTRY_ENABLED === "true", integrations: [ + browserTracingIntegration(), // Filter out errors originating from browser extensions // Note: uses inboundFiltersIntegration (not eventFiltersIntegration) to override defaultIntegrations inboundFiltersIntegration({ denyUrls: [/^chrome-extension:\/\//, /^moz-extension:\/\//] }), thirdPartyErrorFilterIntegration({ - filterKeys: [process.env.NEXT_PUBLIC_SENTRY_APPLICATION_KEY!], + filterKeys: [import.meta.env.VITE_SENTRY_APPLICATION_KEY || ""], behaviour: "drop-error-if-exclusively-contains-third-party-frames" }) ] diff --git a/apps/stats-web/src/App.tsx b/apps/stats-web/src/App.tsx new file mode 100644 index 0000000000..f002f8ab8d --- /dev/null +++ b/apps/stats-web/src/App.tsx @@ -0,0 +1,45 @@ +import "nprogress/nprogress.css"; + +import { useEffect } from "react"; +import { HelmetProvider } from "react-helmet-async"; +import { Outlet, useNavigation } from "react-router-dom"; +import { cn } from "@akashnetwork/ui/utils"; +import NProgress from "nprogress"; + +import GoogleAnalytics from "./components/layout/CustomGoogleAnalytics"; +import Providers from "./components/layout/CustomProviders"; +import { Footer } from "./components/layout/Footer"; +import { Nav } from "./components/layout/Nav"; +import { useTheme } from "./hooks/useTheme"; + +// Configure NProgress +NProgress.configure({ showSpinner: false }); + +export function App() { + const navigation = useNavigation(); + const { theme } = useTheme(); + + // Show progress bar during navigation + useEffect(() => { + if (navigation.state === "loading") { + NProgress.start(); + } else { + NProgress.done(); + } + }, [navigation.state]); + + return ( + +
+ + +
+
+ ); +} diff --git a/apps/stats-web/src/app/(home)/page.tsx b/apps/stats-web/src/app/(home)/page.tsx deleted file mode 100644 index 103224cd26..0000000000 --- a/apps/stats-web/src/app/(home)/page.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { DashboardContainer } from "./DashboardContainer"; - -import PageContainer from "@/components/PageContainer"; - -export default function Home() { - return ( - - - - ); -} diff --git a/apps/stats-web/src/app/addresses/[address]/deployments/[dseq]/page.tsx b/apps/stats-web/src/app/addresses/[address]/deployments/[dseq]/page.tsx deleted file mode 100644 index 8a7faadfc4..0000000000 --- a/apps/stats-web/src/app/addresses/[address]/deployments/[dseq]/page.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import type { Network } from "@akashnetwork/network-store"; -import type { Metadata } from "next"; -import { z } from "zod"; - -import { DeploymentInfo } from "./DeploymentInfo"; - -import PageContainer from "@/components/PageContainer"; -import { Title } from "@/components/Title"; -import { networkId } from "@/config/env-config.schema"; -import { serverFetch } from "@/lib/serverFetch"; -import { UrlService } from "@/lib/urlUtils"; -import { serverApiUrlService } from "@/services/api-url/server-api-url.service"; -import type { DeploymentDetail } from "@/types"; - -const DeploymentDetailPageSchema = z.object({ - params: z.object({ - address: z.string(), - dseq: z.string() - }), - searchParams: z.object({ - network: networkId - }) -}); -type DeploymentDetailPageProps = z.infer; - -export async function generateMetadata({ params: { address, dseq } }: DeploymentDetailPageProps): Promise { - const url = `https://stats.akash.network${UrlService.deployment(address, dseq)}`; - - return { - title: `Deployment ${address}/${dseq}`, - alternates: { - canonical: url - }, - openGraph: { - url - } - }; -} - -async function fetchDeploymentData(address: string, dseq: string, network: Network["id"]): Promise { - const apiUrl = serverApiUrlService.getBaseApiUrlFor(network); - const response = await serverFetch(`${apiUrl}/v1/deployment/${address}/${dseq}`); - - if (!response.ok) { - // This will activate the closest `error.js` Error Boundary - throw new Error("Error fetching address data"); - } - - return response.json(); -} - -export default async function DeploymentDetailPage(props: DeploymentDetailPageProps) { - const { - params: { address, dseq }, - searchParams: { network } - } = DeploymentDetailPageSchema.parse(props); - const deployment = await fetchDeploymentData(address, dseq, network); - - return ( - - Deployment Details - - - - ); -} diff --git a/apps/stats-web/src/app/addresses/[address]/deployments/page.tsx b/apps/stats-web/src/app/addresses/[address]/deployments/page.tsx deleted file mode 100644 index c8dbac057b..0000000000 --- a/apps/stats-web/src/app/addresses/[address]/deployments/page.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import type { Metadata } from "next"; - -import AddressLayout from "../AddressLayout"; -import { AddressDeployments } from "./AddressDeployments"; - -import { UrlService } from "@/lib/urlUtils"; - -export async function generateMetadata({ params: { address } }: IProps): Promise { - const url = `https://stats.akash.network${UrlService.addressDeployments(address)}`; - - return { - title: `Account ${address} deployments`, - alternates: { - canonical: url - }, - openGraph: { - url - } - }; -} - -interface IProps { - params: { address: string }; - searchParams: { [key: string]: string | string[] | undefined }; -} - -export default async function AddressDeploymentsPage({ params: { address } }: IProps) { - return ( - -
- -
-
- ); -} diff --git a/apps/stats-web/src/app/addresses/[address]/page.tsx b/apps/stats-web/src/app/addresses/[address]/page.tsx deleted file mode 100644 index 7ee4e9f979..0000000000 --- a/apps/stats-web/src/app/addresses/[address]/page.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import type { Network } from "@akashnetwork/network-store"; -import type { Metadata } from "next"; -import { z } from "zod"; - -import { AddressInfo } from "./AddressInfo"; -import AddressLayout from "./AddressLayout"; -import { AssetAllocation } from "./AssetAllocation"; -import { AssetList } from "./AssetList"; -import { LatestTransactions } from "./LatestTransactions"; - -import { Title } from "@/components/Title"; -import { networkId } from "@/config/env-config.schema"; -import { createLogger } from "@/lib/createLogger/createLogger"; -import { serverFetch } from "@/lib/serverFetch"; -import { UrlService } from "@/lib/urlUtils"; -import { serverApiUrlService } from "@/services/api-url/server-api-url.service"; -import type { AddressDetail } from "@/types"; - -const logger = createLogger({ context: "AddressDetailPage" }); - -const AddressDetailPageSchema = z.object({ - params: z.object({ - address: z.string() - }), - searchParams: z.object({ - network: networkId - }) -}); -type AddressDetailPageProps = z.infer; - -export async function generateMetadata({ params: { address } }: AddressDetailPageProps): Promise { - const url = `https://stats.akash.network${UrlService.address(address)}`; - - return { - title: `Account ${address}`, - alternates: { - canonical: url - }, - openGraph: { - url - } - }; -} - -async function fetchAddressData(address: string, network: Network["id"]): Promise { - const apiUrl = serverApiUrlService.getBaseApiUrlFor(network); - const response = await serverFetch(`${apiUrl}/v1/addresses/${address}`); - - logger.debug({ event: "FETCHING_ADDRESS_DATA", address, network, status: response.status }); - - if (!response.ok && response.status !== 404) { - throw new Error(`Error fetching address data: ${address}`); - } else if (response.status === 404) { - return null; - } - - return response.json(); -} - -export default async function AddressDetailPage(props: AddressDetailPageProps) { - const { - params: { address }, - searchParams: { network } - } = AddressDetailPageSchema.parse(props); - const addressDetail = await fetchAddressData(address, network); - - if (!addressDetail) { - return ( - -
Address not found or not indexed yet. Please check the address and try again.
-
- ); - } - - return ( - - - -
- - Assets - -
-
- -
-
- -
-
-
- -
- - Latest Transactions - - - -
-
- ); -} diff --git a/apps/stats-web/src/app/addresses/[address]/transactions/page.tsx b/apps/stats-web/src/app/addresses/[address]/transactions/page.tsx deleted file mode 100644 index ea5686aa65..0000000000 --- a/apps/stats-web/src/app/addresses/[address]/transactions/page.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import type { Metadata } from "next"; - -import AddressLayout from "../AddressLayout"; -import { AddressTransactions } from "./AddressTransactions"; - -import { UrlService } from "@/lib/urlUtils"; - -export async function generateMetadata({ params: { address } }: IProps): Promise { - const url = `https://stats.akash.network${UrlService.addressTransactions(address)}`; - - return { - title: `Account ${address} transactions`, - alternates: { - canonical: url - }, - openGraph: { - url - } - }; -} - -interface IProps { - params: { address: string }; - searchParams: { [key: string]: string | string[] | undefined }; -} - -export default async function AddressTransactionsPage({ params: { address } }: IProps) { - return ( - -
- -
-
- ); -} diff --git a/apps/stats-web/src/app/api/healthz/route.ts b/apps/stats-web/src/app/api/healthz/route.ts deleted file mode 100644 index f7d32958fa..0000000000 --- a/apps/stats-web/src/app/api/healthz/route.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { NextResponse } from "next/server"; - -export function GET() { - return NextResponse.json({ status: "ok" }); -} diff --git a/apps/stats-web/src/app/blocks/[height]/error.tsx b/apps/stats-web/src/app/blocks/[height]/error.tsx deleted file mode 100644 index e8f6e668a3..0000000000 --- a/apps/stats-web/src/app/blocks/[height]/error.tsx +++ /dev/null @@ -1,28 +0,0 @@ -"use client"; // Error components must be Client Components - -import { useEffect } from "react"; - -import { useLogger } from "@/hooks/useLogger"; - -export default function Error({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) { - const blockErrorLogger = useLogger("apps/stats-web/src/app/blocks/[height]/errors.tsx"); - - useEffect(() => { - // Log the error to an error reporting service - blockErrorLogger.error(error); - }, [error]); - - return ( -
-

Something went wrong!

- -
- ); -} diff --git a/apps/stats-web/src/app/blocks/[height]/page.tsx b/apps/stats-web/src/app/blocks/[height]/page.tsx deleted file mode 100644 index 57f5d83ea5..0000000000 --- a/apps/stats-web/src/app/blocks/[height]/page.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import type { Network } from "@akashnetwork/network-store"; -import { Card, CardContent, Table, TableBody, TableHead, TableHeader, TableRow } from "@akashnetwork/ui/components"; -import { SearchX } from "lucide-react"; -import type { Metadata } from "next"; -import { z } from "zod"; - -import { BlockInfo } from "./BlockInfo"; - -import { TransactionRow } from "@/components/blockchain/TransactionRow"; -import PageContainer from "@/components/PageContainer"; -import { Title } from "@/components/Title"; -import { networkId } from "@/config/env-config.schema"; -import { createLogger } from "@/lib/createLogger/createLogger"; -import { serverFetch } from "@/lib/serverFetch"; -import { serverApiUrlService } from "@/services/api-url/server-api-url.service"; -import type { BlockDetail } from "@/types"; - -const logger = createLogger({ context: "BlockDetailPage" }); - -const BlockDetailPageSchema = z.object({ - params: z.object({ - height: z.string() - }), - searchParams: z.object({ - network: networkId - }) -}); -type BlockDetailPageProps = z.infer; - -export async function generateMetadata({ params: { height } }: BlockDetailPageProps): Promise { - return { - title: `Block #${height}` - }; -} - -async function fetchBlockData(height: string, network: Network["id"]): Promise { - const apiUrl = serverApiUrlService.getBaseApiUrlFor(network); - const response = await serverFetch(`${apiUrl}/v1/blocks/${height}`); - - if (!response.ok && response.status !== 404) { - logger.error({ event: "BLOCK_FETCH_ERROR", height, network, status: response.status }); - throw new Error(`Error fetching block data: ${height}`); - } else if (response.status === 404) { - logger.debug({ event: "BLOCK_NOT_FOUND", height, network }); - return null; - } - - return response.json(); -} - -export default async function BlockDetailPage(props: BlockDetailPageProps) { - const { - params: { height }, - searchParams: { network } - } = BlockDetailPageSchema.parse(props); - const block = await fetchBlockData(height, network); - - if (!block) { - return ( - - Details for Block #{height} -
Block not found or not indexed yet. Please check the block height and try again.
-
- ); - } - - return ( - - Details for Block #{height} - - - -
- - Transactions - - - - - {block.transactions.length === 0 ? ( -
- -  No transactions -
- ) : ( - - - - Tx Hash - Type - Result - Amount - Fee - Height - Time - - - - - {block.transactions.map(transaction => ( - - ))} - -
- )} -
-
-
-
- ); -} diff --git a/apps/stats-web/src/app/blocks/page.tsx b/apps/stats-web/src/app/blocks/page.tsx deleted file mode 100644 index 977e524389..0000000000 --- a/apps/stats-web/src/app/blocks/page.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import type { Metadata } from "next"; - -import { BlocksTable } from "./BlocksTable"; - -import PageContainer from "@/components/PageContainer"; -import { Title } from "@/components/Title"; - -export const metadata: Metadata = { - title: "Blocks" -}; - -const BlocksPage: React.FunctionComponent = () => { - return ( - - Blocks - - - - ); -}; - -export default BlocksPage; diff --git a/apps/stats-web/src/app/error.tsx b/apps/stats-web/src/app/error.tsx deleted file mode 100644 index 9bc071ce0c..0000000000 --- a/apps/stats-web/src/app/error.tsx +++ /dev/null @@ -1,29 +0,0 @@ -"use client"; // Error components must be Client Components - -import { useEffect } from "react"; -import { Button } from "@akashnetwork/ui/components"; - -import PageContainer from "@/components/PageContainer"; -import { Title } from "@/components/Title"; -import { errorHandler } from "@/services/di"; - -export default function Error({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) { - useEffect(() => { - errorHandler.reportError({ error }); - }, [error]); - - return ( - - Something went wrong! - - - ); -} diff --git a/apps/stats-web/src/app/fonts/Satoshi-Variable.ttf b/apps/stats-web/src/app/fonts/Satoshi-Variable.ttf deleted file mode 100644 index 976e85cb58..0000000000 Binary files a/apps/stats-web/src/app/fonts/Satoshi-Variable.ttf and /dev/null differ diff --git a/apps/stats-web/src/app/fonts/Satoshi-Variable.woff b/apps/stats-web/src/app/fonts/Satoshi-Variable.woff deleted file mode 100644 index f8dcd1d603..0000000000 Binary files a/apps/stats-web/src/app/fonts/Satoshi-Variable.woff and /dev/null differ diff --git a/apps/stats-web/src/app/fonts/Satoshi-Variable.woff2 b/apps/stats-web/src/app/fonts/Satoshi-Variable.woff2 deleted file mode 100644 index b00e833ed4..0000000000 Binary files a/apps/stats-web/src/app/fonts/Satoshi-Variable.woff2 and /dev/null differ diff --git a/apps/stats-web/src/app/global-error.tsx b/apps/stats-web/src/app/global-error.tsx deleted file mode 100644 index efc8812c36..0000000000 --- a/apps/stats-web/src/app/global-error.tsx +++ /dev/null @@ -1,31 +0,0 @@ -"use client"; - -import { useEffect } from "react"; - -import { errorHandler } from "@/services/di"; - -export default function GlobalError({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) { - useEffect(() => { - errorHandler.reportError({ error }); - }, [error]); - - return ( - - -
-

Something went wrong!

- -
- - - ); -} diff --git a/apps/stats-web/src/app/layout.tsx b/apps/stats-web/src/app/layout.tsx deleted file mode 100644 index 87b08d8a28..0000000000 --- a/apps/stats-web/src/app/layout.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import "@akashnetwork/ui/styles"; -import "../styles/index.css"; - -import { cn } from "@akashnetwork/ui/utils"; -import { GeistSans } from "geist/font/sans"; // eslint-disable-line import-x/no-unresolved -import type { Metadata, Viewport } from "next"; -import { cookies } from "next/headers"; - -import GoogleAnalytics from "@/components/layout/CustomGoogleAnalytics"; -import Providers from "@/components/layout/CustomProviders"; -import { Footer } from "@/components/layout/Footer"; -import { Nav } from "@/components/layout/Nav"; -import { customColors } from "@/lib/colors"; - -export const metadata: Metadata = { - title: "Akash Network Stats", - description: "Akash Network Stats", - metadataBase: new URL("https://stats.akash.network"), - openGraph: { - type: "website", - locale: "en_US", - url: "https://stats.akash.network/", - siteName: "Akash Stats", - description: "Akash Network Analytics. The #1 decentralized supercloud.", - images: [ - { - url: "https://stats.akash.network/akash-stats.png", - width: 1200, - height: 630, - alt: "Akash Stats Cover Image" - } - ] - }, - twitter: { - site: "@akashnet", - card: "summary_large_image" - }, - icons: [ - { - url: "/favicon.ico", - href: "/favicon.ico", - rel: "shortcut icon" - }, - { - sizes: "16x16", - url: "/favicon-16x16.png", - href: "/favicon-16x16.png", - rel: "icon", - type: "image/png" - }, - { - sizes: "32x32", - url: "/favicon-32x32.png", - href: "/favicon-32x32.png", - rel: "icon", - type: "image/png" - }, - { - url: "/safari-pinned-tab.svg", - href: "/safari-pinned-tab.svg", - rel: "mask-icon", - color: customColors.dark - }, - { - url: "/apple-touch-icon.png", - href: "/apple-touch-icon.png", - rel: "apple-touch-icon" - } - ] -}; - -export const viewport: Viewport = { - minimumScale: 1, - initialScale: 1, - width: "device-width" -}; - -/** - * Get the theme from the cookie - * next-themes doesn't support SSR - * https://github.com/pacocoursey/next-themes/issues/169 - */ -function getTheme() { - const cookieStore = cookies(); - const themeCookie = cookieStore.get("theme"); - const theme = themeCookie ? themeCookie.value : "system"; - return theme; -} - -export default function RootLayout({ children }: { children: React.ReactNode }) { - const theme = getTheme() as string; - - return ( - - - - - -