diff --git a/api/src/caching/helpers.ts b/api/src/caching/helpers.ts index 9b313ef11..97dc43f68 100644 --- a/api/src/caching/helpers.ts +++ b/api/src/caching/helpers.ts @@ -29,19 +29,19 @@ export const Memoize = (options?: MemoizeOptions) => (target: object, propertyNa export async function cacheResponse(seconds: number, key: string, refreshRequest: () => Promise, keepData?: boolean): Promise { const duration = seconds * 1000; const cachedObject = cacheEngine.getFromCache(key) as CachedObject | undefined; - console.log(`Cache key: ${key}`); + // console.log(`Cache key: ${key}`); // If first time or expired, must refresh data if not already refreshing const cacheExpired = Math.abs(differenceInSeconds(cachedObject?.date, new Date())) > seconds; if ((!cachedObject || cacheExpired) && !(key in pendingRequests)) { - console.log(`Making request: ${key}`); + // console.log(`Making request: ${key}`); pendingRequests[key] = refreshRequest() .then((data) => { cacheEngine.storeInCache(key, { date: new Date(), data: data }, keepData ? undefined : duration); return data; }) .catch((err) => { - console.log(`Error making cache request ${err}`); + // console.log(`Error making cache request ${err}`); Sentry.captureException(err); }) .finally(() => { @@ -51,10 +51,10 @@ export async function cacheResponse(seconds: number, key: string, refreshRequ // If there is data in cache, return it even if it is expired. Otherwise, wait for the refresh request to finish if (cachedObject) { - console.log(`Cache hit: ${key}`); + // console.log(`Cache hit: ${key}`); return cachedObject.data; } else { - console.log(`Waiting for pending request: ${key}`); + // console.log(`Waiting for pending request: ${key}`); return (await pendingRequests[key]) as T; } } diff --git a/api/src/middlewares/userMiddleware.ts b/api/src/middlewares/userMiddleware.ts index 6399df35b..63a98b1c3 100644 --- a/api/src/middlewares/userMiddleware.ts +++ b/api/src/middlewares/userMiddleware.ts @@ -21,8 +21,7 @@ export const kvStore = { export const requiredUserMiddleware = verifyRsaJwt({ jwksUri: env.Auth0JWKSUri, - kvStore: kvStore, - verbose: true + kvStore: kvStore }); export const optionalUserMiddleware = verifyRsaJwt({ diff --git a/api/src/routers/userRouter.ts b/api/src/routers/userRouter.ts index 4f91b5702..fc3848423 100644 --- a/api/src/routers/userRouter.ts +++ b/api/src/routers/userRouter.ts @@ -139,7 +139,6 @@ userOptionalRouter.get("/template/:id", async (c) => { if (!uuid.validate(templateId)) { return c.text("Invalid template id", 400); - return; } const template = await getTemplateById(templateId, userId); diff --git a/api/src/services/db/templateService.ts b/api/src/services/db/templateService.ts index 524ddb5f7..20bf38c77 100644 --- a/api/src/services/db/templateService.ts +++ b/api/src/services/db/templateService.ts @@ -67,7 +67,7 @@ export async function saveTemplate( ) { let template = await Template.findOne({ where: { - id: id, + id: id || null, userId: userId } }); diff --git a/api/src/verify-rsa-jwt-cloudflare-worker-main/hono-middleware.ts b/api/src/verify-rsa-jwt-cloudflare-worker-main/hono-middleware.ts index 7ef1ed25c..569b940ea 100644 --- a/api/src/verify-rsa-jwt-cloudflare-worker-main/hono-middleware.ts +++ b/api/src/verify-rsa-jwt-cloudflare-worker-main/hono-middleware.ts @@ -1,6 +1,6 @@ -import type { Context, MiddlewareHandler } from 'hono'; -import type { GeneralKeyValueStore, VerificationResult } from '.'; -import { getJwks, useKVStore, verify } from '.'; +import type { Context, MiddlewareHandler } from "hono"; +import type { GeneralKeyValueStore, VerificationResult } from "."; +import { getJwks, useKVStore, verify } from "."; export type VerifyRsaJwtConfig = { jwksUri?: string; @@ -10,25 +10,24 @@ export type VerifyRsaJwtConfig = { optional?: boolean; }; -const PAYLOAD_KEY = 'verifyRsaJwtPayload'; +const PAYLOAD_KEY = "verifyRsaJwtPayload"; export function verifyRsaJwt(config?: VerifyRsaJwtConfig): MiddlewareHandler { return async (ctx: Context, next) => { - const jwtToken = ctx.req.headers - .get('Authorization') - ?.replace(/Bearer\s+/i, ''); - if (!jwtToken || jwtToken.length === 0) { - return new Response('Bad Request', { status: 400 }); - } try { + const jwtToken = ctx.req.headers.get("Authorization")?.replace(/Bearer\s+/i, ""); + if (!jwtToken || jwtToken.length === 0) { + throw new Error("JWT token not found in Authorization header"); + } + const jwks = await getJwks( config?.jwksUri || ctx.env.JWKS_URI, useKVStore(config?.kvStore || ctx.env?.VERIFY_RSA_JWT), - ctx.env?.VERIFY_RSA_JWT_JWKS_CACHE_KEY, + ctx.env?.VERIFY_RSA_JWT_JWKS_CACHE_KEY ); const result = await verify(jwtToken, jwks); if (result.payload === null) { - throw new Error('Invalid token'); + throw new Error("Invalid token"); } // Custom validator that should throw an error if the payload is invalid. @@ -38,8 +37,7 @@ export function verifyRsaJwt(config?: VerifyRsaJwtConfig): MiddlewareHandler { ctx.set(PAYLOAD_KEY, result.payload); await next(); } catch (error) { - config?.verbose && - console.error({ message: 'verification failed', error }); + config?.verbose && console.error({ message: "verification failed", error }); if (config?.optional) { await next(); diff --git a/api/webpack.dev.js b/api/webpack.dev.js index cbb461809..181463833 100644 --- a/api/webpack.dev.js +++ b/api/webpack.dev.js @@ -8,13 +8,14 @@ module.exports = { entry: "./src/index.ts", mode: NODE_ENV, target: "node", + devtool: "source-map", output: { path: path.resolve(__dirname, "dist"), - filename: "server.js", + filename: "server.js" }, resolve: { extensions: [".ts", ".js"], - alias: hq.get("webpack"), + alias: hq.get("webpack") }, externals: [nodeExternals()], module: { @@ -23,9 +24,9 @@ module.exports = { test: /\.(ts|js)x?$/, exclude: /node_modules/, loader: "ts-loader", - options: { configFile: "tsconfig.build.json"} - }, - ], + options: { configFile: "tsconfig.build.json" } + } + ] }, - plugins: [new NodemonPlugin()], + plugins: [new NodemonPlugin()] }; diff --git a/api/webpack.prod.js b/api/webpack.prod.js index bc38b1f79..a025db385 100644 --- a/api/webpack.prod.js +++ b/api/webpack.prod.js @@ -1,6 +1,5 @@ const path = require("path"); const { NODE_ENV = "production" } = process.env; -const NodemonPlugin = require("nodemon-webpack-plugin"); const nodeExternals = require("webpack-node-externals"); const hq = require("alias-hq"); @@ -10,11 +9,11 @@ module.exports = { target: "node", output: { path: path.resolve(__dirname, "dist"), - filename: "server.js", + filename: "server.js" }, resolve: { extensions: [".ts", ".js"], - alias: hq.get("webpack"), + alias: hq.get("webpack") }, externals: [nodeExternals()], module: { @@ -23,9 +22,9 @@ module.exports = { test: /\.(ts|js)x?$/, exclude: /node_modules/, loader: "ts-loader", - options: { configFile: "tsconfig.build.json"} - }, - ], + options: { configFile: "tsconfig.build.json" } + } + ] }, - plugins: [new NodemonPlugin()], + plugins: [] }; diff --git a/deploy-web/next-env.d.ts b/deploy-web/next-env.d.ts index fd36f9494..4f11a03dc 100644 --- a/deploy-web/next-env.d.ts +++ b/deploy-web/next-env.d.ts @@ -1,6 +1,5 @@ /// /// -/// // NOTE: This file should not be edited // see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/deploy-web/next.config.js b/deploy-web/next.config.js index 43634f67b..bac8481af 100644 --- a/deploy-web/next.config.js +++ b/deploy-web/next.config.js @@ -25,6 +25,7 @@ const moduleExports = { typescript: { tsconfigPath: "./tsconfig.json" }, + transpilePackages: ["geist"], // experimental: { // // outputStandalone: true, // externalDir: true // to make the import from shared parent folder work https://github.com/vercel/next.js/issues/9474#issuecomment-810212174 diff --git a/deploy-web/package-lock.json b/deploy-web/package-lock.json index 292bde5d0..54010badb 100644 --- a/deploy-web/package-lock.json +++ b/deploy-web/package-lock.json @@ -12,7 +12,7 @@ "dependencies": { "@akashnetwork/akash-api": "^1.0.3", "@akashnetwork/akashjs": "^0.6.0", - "@auth0/nextjs-auth0": "^3.1.0", + "@auth0/nextjs-auth0": "^3.5.0", "@cosmjs/encoding": "^0.29.5", "@cosmjs/stargate": "^0.29.5", "@cosmos-kit/cosmostation-extension": "^2.6.4", @@ -61,7 +61,7 @@ "@stripe/stripe-js": "^1.41.0", "@tanstack/react-table": "^8.13.2", "@textea/json-viewer": "^3.0.0", - "auth0": "^2.42.0", + "auth0": "^4.3.1", "axios": "^0.27.2", "chain-registry": "^1.20.0", "class-variance-authority": "^0.7.0", @@ -71,6 +71,7 @@ "date-fns": "^2.29.3", "eslint-config-next": "^13.4.18", "file-saver": "^2.0.5", + "geist": "^1.3.0", "http-proxy": "^1.18.1", "iconoir-react": "^7.3.0", "jotai": "^2.0.4", @@ -83,7 +84,7 @@ "lucide-react": "^0.292.0", "material-ui-popup-state": "^4.0.2", "nanoid": "^3.3.4", - "next": "^14.1.0", + "next": "^13.4.19", "next-nprogress-bar": "^2.1.2", "next-pwa": "^5.6.0", "next-qrcode": "^2.1.0", @@ -512,12 +513,12 @@ } }, "node_modules/@auth0/nextjs-auth0": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@auth0/nextjs-auth0/-/nextjs-auth0-3.1.0.tgz", - "integrity": "sha512-K00wD7hlMRcmDgDVRN5pP/SLV7odBnvAXvput1kJHGRJUp9FwFzg6BEGEWVHhr2YOXcn+VdVMMjXlmWYV+aZ1A==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@auth0/nextjs-auth0/-/nextjs-auth0-3.5.0.tgz", + "integrity": "sha512-uFZEE2QQf1zU+jRK2fwqxRQt+WSqDPYF2tnr7d6BEa7b6L6tpPJ3evzoImbWSY1a7gFdvD7RD/Rvrsx7B5CKVg==", "dependencies": { "@panva/hkdf": "^1.0.2", - "cookie": "^0.5.0", + "cookie": "^0.6.0", "debug": "^4.3.4", "joi": "^17.6.0", "jose": "^4.9.2", @@ -533,14 +534,6 @@ "next": ">=10" } }, - "node_modules/@auth0/nextjs-auth0/node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", @@ -7094,9 +7087,9 @@ } }, "node_modules/@next/env": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.0.tgz", - "integrity": "sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw==" + "version": "13.4.19", + "resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.19.tgz", + "integrity": "sha512-FsAT5x0jF2kkhNkKkukhsyYOrRqtSxrEhfliniIq0bwWbuXLgyt3Gv0Ml+b91XwjwArmuP7NxCiGd++GGKdNMQ==" }, "node_modules/@next/eslint-plugin-next": { "version": "13.4.18", @@ -7126,9 +7119,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.0.tgz", - "integrity": "sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==", + "version": "13.4.19", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.19.tgz", + "integrity": "sha512-vv1qrjXeGbuF2mOkhkdxMDtv9np7W4mcBtaDnHU+yJG+bBwa6rYsYSCI/9Xm5+TuF5SbZbrWO6G1NfTh1TMjvQ==", "cpu": [ "arm64" ], @@ -7141,9 +7134,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.0.tgz", - "integrity": "sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==", + "version": "13.4.19", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.19.tgz", + "integrity": "sha512-jyzO6wwYhx6F+7gD8ddZfuqO4TtpJdw3wyOduR4fxTUCm3aLw7YmHGYNjS0xRSYGAkLpBkH1E0RcelyId6lNsw==", "cpu": [ "x64" ], @@ -7156,9 +7149,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.0.tgz", - "integrity": "sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==", + "version": "13.4.19", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.19.tgz", + "integrity": "sha512-vdlnIlaAEh6H+G6HrKZB9c2zJKnpPVKnA6LBwjwT2BTjxI7e0Hx30+FoWCgi50e+YO49p6oPOtesP9mXDRiiUg==", "cpu": [ "arm64" ], @@ -7171,9 +7164,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.0.tgz", - "integrity": "sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g==", + "version": "13.4.19", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.19.tgz", + "integrity": "sha512-aU0HkH2XPgxqrbNRBFb3si9Ahu/CpaR5RPmN2s9GiM9qJCiBBlZtRTiEca+DC+xRPyCThTtWYgxjWHgU7ZkyvA==", "cpu": [ "arm64" ], @@ -7186,9 +7179,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.0.tgz", - "integrity": "sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ==", + "version": "13.4.19", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.19.tgz", + "integrity": "sha512-htwOEagMa/CXNykFFeAHHvMJeqZfNQEoQvHfsA4wgg5QqGNqD5soeCer4oGlCol6NGUxknrQO6VEustcv+Md+g==", "cpu": [ "x64" ], @@ -7201,9 +7194,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.0.tgz", - "integrity": "sha512-rbaIYFt2X9YZBSbH/CwGAjbBG2/MrACCVu2X0+kSykHzHnYH5FjHxwXLkcoJ10cX0aWCEynpu+rP76x0914atg==", + "version": "13.4.19", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.19.tgz", + "integrity": "sha512-4Gj4vvtbK1JH8ApWTT214b3GwUh9EKKQjY41hH/t+u55Knxi/0wesMzwQRhppK6Ddalhu0TEttbiJ+wRcoEj5Q==", "cpu": [ "x64" ], @@ -7216,9 +7209,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.0.tgz", - "integrity": "sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==", + "version": "13.4.19", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.19.tgz", + "integrity": "sha512-bUfDevQK4NsIAHXs3/JNgnvEY+LRyneDN788W2NYiRIIzmILjba7LaQTfihuFawZDhRtkYCv3JDC3B4TwnmRJw==", "cpu": [ "arm64" ], @@ -7231,9 +7224,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.0.tgz", - "integrity": "sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==", + "version": "13.4.19", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.19.tgz", + "integrity": "sha512-Y5kikILFAr81LYIFaw6j/NrOtmiM4Sf3GtOc0pn50ez2GCkr+oejYuKGcwAwq3jiTKuzF6OF4iT2INPoxRycEA==", "cpu": [ "ia32" ], @@ -7246,9 +7239,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.0.tgz", - "integrity": "sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==", + "version": "13.4.19", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.19.tgz", + "integrity": "sha512-YzA78jBDXMYiINdPdJJwGgPNT3YqBNNGhsthsDoWHL9p24tEJn9ViQf/ZqTbwSpX/RrkPupLfuuTH2sf73JBAw==", "cpu": [ "x64" ], @@ -12171,11 +12164,19 @@ "node": ">=6" } }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "peer": true + }, "node_modules/@swc/helpers": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz", - "integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "peer": true, "dependencies": { + "@swc/counter": "^0.1.3", "tslib": "^2.4.0" } }, @@ -12348,14 +12349,6 @@ "node": ">=6" } }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "engines": { - "node": ">= 6" - } - }, "node_modules/@types/auth0": { "version": "2.35.3", "resolved": "https://registry.npmjs.org/@types/auth0/-/auth0-2.35.3.tgz", @@ -12407,15 +12400,6 @@ "@babel/types": "^7.3.0" } }, - "node_modules/@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, "node_modules/@types/connect": { "version": "3.4.35", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", @@ -12548,44 +12532,6 @@ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==" }, - "node_modules/@types/express": { - "version": "4.17.13", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", - "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.18", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-jwt": { - "version": "0.0.42", - "resolved": "https://registry.npmjs.org/@types/express-jwt/-/express-jwt-0.0.42.tgz", - "integrity": "sha512-WszgUddvM1t5dPpJ3LhWNH8kfNN8GPIBrAGxgIYXVCEGx6Bx4A036aAuf/r5WH9DIEdlmp7gHOYvSM6U87B0ag==", - "dependencies": { - "@types/express": "*", - "@types/express-unless": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.30", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.30.tgz", - "integrity": "sha512-gstzbTWro2/nFed1WXtf+TtrpwxH7Ggs4RLYTLbeVgIkUQOI3WG/JKjgeOU1zXDvezllupjrf8OPIdvTbIaVOQ==", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" - } - }, - "node_modules/@types/express-unless": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@types/express-unless/-/express-unless-0.5.3.tgz", - "integrity": "sha512-TyPLQaF6w8UlWdv4gj8i46B+INBVzURBNRahCozCSXfsK2VTlL1wNyTlMKw817VHygBtlcl5jfnPadlydr06Yw==", - "dependencies": { - "@types/express": "*" - } - }, "node_modules/@types/file-saver": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.5.tgz", @@ -12673,11 +12619,6 @@ "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==" }, - "node_modules/@types/mime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.0.tgz", - "integrity": "sha512-fccbsHKqFDXClBZTDLA43zl0+TbxyIwyzIzwwhvoJvhNjOErCdeX2xJbURimv2EbSVUGav001PaCJg4mZxMl4w==" - }, "node_modules/@types/minimatch": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", @@ -12709,16 +12650,6 @@ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, - "node_modules/@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" - }, - "node_modules/@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" - }, "node_modules/@types/react": { "version": "17.0.39", "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.39.tgz", @@ -12787,15 +12718,6 @@ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" }, - "node_modules/@types/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", - "dependencies": { - "@types/mime": "*", - "@types/node": "*" - } - }, "node_modules/@types/trusted-types": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz", @@ -14369,33 +14291,27 @@ } }, "node_modules/auth0": { - "version": "2.44.1", - "resolved": "https://registry.npmjs.org/auth0/-/auth0-2.44.1.tgz", - "integrity": "sha512-+/lloZ2YGa8Epf2e1TRhyeXNEN1xHd4mw7arhYvD0ZY83ZOZxsHtaVyaOXuSL+rp0uqXHjyNjAbkGeyYxTUe7Q==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/auth0/-/auth0-4.3.1.tgz", + "integrity": "sha512-VuR/PdNHwXD9QiYUZf8UN0qiv07yJWHKfF2yEhePaPCrpwbEm6lHM6dkN7ylOdseogCkGmvchxYGMga0IPNrCA==", "dependencies": { - "axios": "^0.27.2", - "form-data": "^3.0.1", - "jsonwebtoken": "^8.5.1", - "jwks-rsa": "^1.12.1", - "lru-memoizer": "^2.1.4", - "rest-facade": "^1.16.3", - "retry": "^0.13.1" + "jose": "^4.13.2", + "uuid": "^9.0.0" }, "engines": { - "node": ">=8.3.0" + "node": ">=18" } }, - "node_modules/auth0/node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" + "node_modules/auth0/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" } }, "node_modules/autoprefixer": { @@ -14885,11 +14801,6 @@ "ieee754": "^1.1.13" } }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" - }, "node_modules/buffer-from": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-0.1.2.tgz", @@ -15024,68 +14935,6 @@ "node": ">=0.8.0" } }, - "node_modules/change-case": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/change-case/-/change-case-2.3.1.tgz", - "integrity": "sha512-3HE5jrTqqn9jeKzD0+yWi7FU4OMicLbwB57ph4bpwEn5jGi3hZug5WjZjnBD2RY7YyTKAAck86ACfShXUWJKLg==", - "dependencies": { - "camel-case": "^1.1.1", - "constant-case": "^1.1.0", - "dot-case": "^1.1.0", - "is-lower-case": "^1.1.0", - "is-upper-case": "^1.1.0", - "lower-case": "^1.1.1", - "lower-case-first": "^1.0.0", - "param-case": "^1.1.0", - "pascal-case": "^1.1.0", - "path-case": "^1.1.0", - "sentence-case": "^1.1.1", - "snake-case": "^1.1.0", - "swap-case": "^1.1.0", - "title-case": "^1.1.0", - "upper-case": "^1.1.1", - "upper-case-first": "^1.1.0" - } - }, - "node_modules/change-case/node_modules/camel-case": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-1.2.2.tgz", - "integrity": "sha512-rUug78lL8mqStaLehmH2F0LxMJ2TM9fnPFxb+gFkgyUjUM/1o2wKTQtalypHnkb2cFwH/DENBw7YEAOYLgSMxQ==", - "dependencies": { - "sentence-case": "^1.1.1", - "upper-case": "^1.1.1" - } - }, - "node_modules/change-case/node_modules/dot-case": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-1.1.2.tgz", - "integrity": "sha512-NzEIt12UjECXi6JZ/R/nBey6EE1qCN0yUTEFaPIaKW0AcOEwlKqujtcJVbtSfLNnj3CDoXLQyli79vAaqohyvw==", - "dependencies": { - "sentence-case": "^1.1.2" - } - }, - "node_modules/change-case/node_modules/lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==" - }, - "node_modules/change-case/node_modules/param-case": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-1.1.2.tgz", - "integrity": "sha512-gksk6zeZQxwBm1AHsKh+XDFsTGf1LvdZSkkpSIkfDtzW+EQj/P2PBgNb3Cs0Y9Xxqmbciv2JZe3fWU6Xbher+Q==", - "dependencies": { - "sentence-case": "^1.1.2" - } - }, - "node_modules/change-case/node_modules/pascal-case": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-1.1.2.tgz", - "integrity": "sha512-QWlbdQHdKWlcyTEuv/M0noJtlCa7qTmg5QFAqhx5X9xjAfCU1kXucL+rcOmd2HliESuRLIOz8521RAW/yhuQog==", - "dependencies": { - "camel-case": "^1.1.1", - "upper-case-first": "^1.1.0" - } - }, "node_modules/character-entities": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", @@ -15569,11 +15418,6 @@ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -15592,15 +15436,6 @@ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==" }, - "node_modules/constant-case": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-1.1.2.tgz", - "integrity": "sha512-FQ/HuOuSnX6nIF8OnofRWj+KnOpGAHXQpOKHmsL1sAnuLwu6r5mHGK+mJc0SkHkbmNfcU/SauqXLTEOL1JQfJA==", - "dependencies": { - "snake-case": "^1.1.0", - "upper-case": "^1.1.1" - } - }, "node_modules/convert-source-map": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", @@ -15609,16 +15444,19 @@ "safe-buffer": "~5.1.1" } }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cookie-es": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.0.0.tgz", "integrity": "sha512-mWYvfOLrfEc996hlKcdABeIiPHUPC6DM2QYZdGGOvhOTbA3tjm2eBwqlJpoFdjC89NI4Qt6h0Pu06Mp+1Pj5OQ==" }, - "node_modules/cookiejar": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", - "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==" - }, "node_modules/copy-to-clipboard": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", @@ -16437,14 +16275,6 @@ "node": ">= 6" } }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, "node_modules/ejs": { "version": "3.1.9", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", @@ -17717,15 +17547,6 @@ "node": ">=0.4.x" } }, - "node_modules/formidable": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", - "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==", - "deprecated": "Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau", - "funding": { - "url": "https://ko-fi.com/tunnckoCore/commissions" - } - }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -17845,6 +17666,14 @@ "node": ">=10" } }, + "node_modules/geist": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/geist/-/geist-1.3.0.tgz", + "integrity": "sha512-IoGBfcqVEYB4bEwsfHd35jF4+X9LHRPYZymHL4YOltHSs9LJa24DYs1Z7rEMQ/lsEvaAIc61Y9aUxgcJaQ8lrg==", + "peerDependencies": { + "next": ">=13.2.0 <15.0.0-0" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -17973,8 +17802,7 @@ "node_modules/glob-to-regexp": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "peer": true + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" }, "node_modules/globals": { "version": "11.12.0", @@ -18316,19 +18144,6 @@ "node": ">=8.0.0" } }, - "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/http-shutdown": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/http-shutdown/-/http-shutdown-1.2.2.tgz", @@ -18751,19 +18566,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-lower-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-1.1.3.tgz", - "integrity": "sha512-+5A1e/WJpLLXZEDlgz4G//WYSHyQBD32qa4Jd3Lw06qQlv3fJHnp3YIHjTQSGzHMgzmVKz2ZP3rBxTHkPw/lxA==", - "dependencies": { - "lower-case": "^1.1.0" - } - }, - "node_modules/is-lower-case/node_modules/lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==" - }, "node_modules/is-map": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", @@ -18967,14 +18769,6 @@ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" }, - "node_modules/is-upper-case": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-1.1.2.tgz", - "integrity": "sha512-GQYSJMgfeAmVwh9ixyk888l7OIhNAGKtY6QA+IrWlu9MDTCaXmeozOZ2S9Knj7bQwBO/H6J2kb+pbyTUiMNbsw==", - "dependencies": { - "upper-case": "^1.1.0" - } - }, "node_modules/is-weakmap": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", @@ -19498,35 +19292,6 @@ "node": "*" } }, - "node_modules/jsonwebtoken": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", - "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", - "dependencies": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=4", - "npm": ">=1.4.28" - } - }, - "node_modules/jsonwebtoken/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "bin": { - "semver": "bin/semver" - } - }, "node_modules/jsrsasign": { "version": "10.6.1", "resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-10.6.1.tgz", @@ -19547,50 +19312,6 @@ "node": ">=4.0" } }, - "node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jwks-rsa": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-1.12.3.tgz", - "integrity": "sha512-cFipFDeYYaO9FhhYJcZWX/IyZgc0+g316rcHnDpT2dNRNIE/lMOmWKKqp09TkJoYlNFzrEVODsR4GgXJMgWhnA==", - "dependencies": { - "@types/express-jwt": "0.0.42", - "axios": "^0.21.1", - "debug": "^4.1.0", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "jsonwebtoken": "^8.5.1", - "limiter": "^1.1.5", - "lru-memoizer": "^2.1.2", - "ms": "^2.1.2", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/jwks-rsa/node_modules/axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "dependencies": { - "follow-redirects": "^1.14.0" - } - }, - "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, "node_modules/keccak": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.4.tgz", @@ -19752,11 +19473,6 @@ "node": ">=10" } }, - "node_modules/limiter": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", - "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" - }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -19886,11 +19602,6 @@ "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==", "dev": true }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" - }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -19906,56 +19617,27 @@ "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" - }, "node_modules/lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" - }, "node_modules/lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" }, - "node_modules/lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" - }, - "node_modules/lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" - }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" - }, "node_modules/lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -19986,19 +19668,6 @@ "loose-envify": "cli.js" } }, - "node_modules/lower-case-first": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/lower-case-first/-/lower-case-first-1.0.2.tgz", - "integrity": "sha512-UuxaYakO7XeONbKrZf5FEgkantPf5DUqDayzP5VXZrtRPdH86s4kN47I8B3TW10S4QKiE3ziHNf3kRN//okHjA==", - "dependencies": { - "lower-case": "^1.1.2" - } - }, - "node_modules/lower-case-first/node_modules/lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==" - }, "node_modules/lowlight": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-2.9.0.tgz", @@ -20024,29 +19693,6 @@ "node": ">=10" } }, - "node_modules/lru-memoizer": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.1.4.tgz", - "integrity": "sha512-IXAq50s4qwrOBrXJklY+KhgZF+5y98PDaNo0gi/v2KQBFLyWr+JyFvijZXkGKjQj/h9c0OwoE+JZbwUXce76hQ==", - "dependencies": { - "lodash.clonedeep": "^4.5.0", - "lru-cache": "~4.0.0" - } - }, - "node_modules/lru-memoizer/node_modules/lru-cache": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", - "integrity": "sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==", - "dependencies": { - "pseudomap": "^1.0.1", - "yallist": "^2.0.0" - } - }, - "node_modules/lru-memoizer/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" - }, "node_modules/lucide-react": { "version": "0.292.0", "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.292.0.tgz", @@ -20393,14 +20039,6 @@ "node": ">= 8" } }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/micromark": { "version": "3.0.10", "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.0.10.tgz", @@ -21199,34 +20837,35 @@ "peer": true }, "node_modules/next": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/next/-/next-14.1.0.tgz", - "integrity": "sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==", + "version": "13.4.19", + "resolved": "https://registry.npmjs.org/next/-/next-13.4.19.tgz", + "integrity": "sha512-HuPSzzAbJ1T4BD8e0bs6B9C1kWQ6gv8ykZoRWs5AQoiIuqbGHHdQO7Ljuvg05Q0Z24E2ABozHe6FxDvI6HfyAw==", "dependencies": { - "@next/env": "14.1.0", - "@swc/helpers": "0.5.2", + "@next/env": "13.4.19", + "@swc/helpers": "0.5.1", "busboy": "1.6.0", - "caniuse-lite": "^1.0.30001579", - "graceful-fs": "^4.2.11", - "postcss": "8.4.31", - "styled-jsx": "5.1.1" + "caniuse-lite": "^1.0.30001406", + "postcss": "8.4.14", + "styled-jsx": "5.1.1", + "watchpack": "2.4.0", + "zod": "3.21.4" }, "bin": { "next": "dist/bin/next" }, "engines": { - "node": ">=18.17.0" + "node": ">=16.8.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "14.1.0", - "@next/swc-darwin-x64": "14.1.0", - "@next/swc-linux-arm64-gnu": "14.1.0", - "@next/swc-linux-arm64-musl": "14.1.0", - "@next/swc-linux-x64-gnu": "14.1.0", - "@next/swc-linux-x64-musl": "14.1.0", - "@next/swc-win32-arm64-msvc": "14.1.0", - "@next/swc-win32-ia32-msvc": "14.1.0", - "@next/swc-win32-x64-msvc": "14.1.0" + "@next/swc-darwin-arm64": "13.4.19", + "@next/swc-darwin-x64": "13.4.19", + "@next/swc-linux-arm64-gnu": "13.4.19", + "@next/swc-linux-arm64-musl": "13.4.19", + "@next/swc-linux-x64-gnu": "13.4.19", + "@next/swc-linux-x64-musl": "13.4.19", + "@next/swc-win32-arm64-msvc": "13.4.19", + "@next/swc-win32-ia32-msvc": "13.4.19", + "@next/swc-win32-x64-msvc": "13.4.19" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -21302,10 +20941,18 @@ "react-dom": "*" } }, + "node_modules/next/node_modules/@swc/helpers": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz", + "integrity": "sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==", + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/next/node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "version": "8.4.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", + "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", "funding": [ { "type": "opencollective", @@ -21314,14 +20961,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "nanoid": "^3.3.6", + "nanoid": "^3.3.4", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -21329,6 +20972,14 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/next/node_modules/zod": { + "version": "3.21.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz", + "integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/nextjs-google-analytics": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/nextjs-google-analytics/-/nextjs-google-analytics-2.3.3.tgz", @@ -21932,14 +21583,6 @@ "node": ">= 14" } }, - "node_modules/path-case": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/path-case/-/path-case-1.1.2.tgz", - "integrity": "sha512-2snAGA6xVRqTuTPa40bn0iEpYtVK6gEqeyS/63dqpm5pGlesOv6EmRcnB9Rr6eAnAC2Wqlbz0tqgJZryttxhxg==", - "dependencies": { - "sentence-case": "^1.1.2" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -22548,11 +22191,6 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" - }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -23461,41 +23099,6 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, - "node_modules/rest-facade": { - "version": "1.16.3", - "resolved": "https://registry.npmjs.org/rest-facade/-/rest-facade-1.16.3.tgz", - "integrity": "sha512-9BQTPLiwg23XZwcWi0ys1wTizfc//0b2G3U6vBWcgqh56ozs2K6CD+Jw4DYcw3AqdPQN7jj8nzRUcUXFVGzb0Q==", - "dependencies": { - "change-case": "^2.3.0", - "deepmerge": "^3.2.0", - "lodash.get": "^4.4.2", - "superagent": "^5.1.1" - }, - "peerDependencies": { - "superagent-proxy": "^3.0.0" - }, - "peerDependenciesMeta": { - "superagent-proxy": { - "optional": true - } - } - }, - "node_modules/rest-facade/node_modules/deepmerge": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.3.0.tgz", - "integrity": "sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "engines": { - "node": ">= 4" - } - }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -23881,19 +23484,6 @@ "node": ">=10" } }, - "node_modules/sentence-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-1.1.3.tgz", - "integrity": "sha512-laa/UDTPXsrQnoN/Kc8ZO7gTeEjMsuPiDgUCk9N0iINRZvqAMCTXjGl8+tD27op1eF/JHbdUlEUmovDh6AX7sA==", - "dependencies": { - "lower-case": "^1.1.1" - } - }, - "node_modules/sentence-case/node_modules/lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==" - }, "node_modules/serialize-javascript": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", @@ -24099,14 +23689,6 @@ "node": ">=8" } }, - "node_modules/snake-case": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-1.1.2.tgz", - "integrity": "sha512-oapUKC+qulnUIN+/O7Tbl2msi9PQvJeivGN9RNbygxzI2EOY0gA96i8BJLYnGUWSLGcYtyW4YYqnGTZEySU/gg==", - "dependencies": { - "sentence-case": "^1.1.2" - } - }, "node_modules/sonic-boom": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-2.8.0.tgz", @@ -24493,65 +24075,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/superagent": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-5.3.1.tgz", - "integrity": "sha512-wjJ/MoTid2/RuGCOFtlacyGNxN9QLMgcpYLDQlWFIhhdJ93kNscFonGvrpAHSCVjRVj++DGCglocF7Aej1KHvQ==", - "deprecated": "Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at .", - "dependencies": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.2", - "debug": "^4.1.1", - "fast-safe-stringify": "^2.0.7", - "form-data": "^3.0.0", - "formidable": "^1.2.2", - "methods": "^1.1.2", - "mime": "^2.4.6", - "qs": "^6.9.4", - "readable-stream": "^3.6.0", - "semver": "^7.3.2" - }, - "engines": { - "node": ">= 7.0.0" - } - }, - "node_modules/superagent/node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/superagent/node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/superagent/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/superstruct": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-0.14.2.tgz", @@ -24579,20 +24102,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/swap-case": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/swap-case/-/swap-case-1.1.2.tgz", - "integrity": "sha512-BAmWG6/bx8syfc6qXPprof3Mn5vQgf5dwdUNJhsNqU9WdPt5P+ES/wQ5bxfijy8zwZgZZHslC3iAsxsuQMCzJQ==", - "dependencies": { - "lower-case": "^1.1.1", - "upper-case": "^1.1.1" - } - }, - "node_modules/swap-case/node_modules/lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==" - }, "node_modules/swr": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/swr/-/swr-2.1.5.tgz", @@ -24956,15 +24465,6 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, - "node_modules/title-case": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/title-case/-/title-case-1.1.2.tgz", - "integrity": "sha512-xYbo5Um5MBgn24xJSK+x5hZ8ehuGXTVhgx32KJCThHRHwpyIb1lmABi1DH5VvN9E7rNEquPjz//rF/tZQd7mjQ==", - "dependencies": { - "sentence-case": "^1.1.1", - "upper-case": "^1.0.3" - } - }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -25646,19 +25146,6 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/upper-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==" - }, - "node_modules/upper-case-first": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz", - "integrity": "sha512-wINKYvI3Db8dtjikdAqoBbZoP6Q+PZUyfMR7pmwHzjC2quzSkUq5DmPrTtPEqHaz8AGtmsB4TqwapMTM1QAQOQ==", - "dependencies": { - "upper-case": "^1.1.1" - } - }, "node_modules/uqr": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/uqr/-/uqr-0.1.2.tgz", @@ -26003,7 +25490,6 @@ "version": "2.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "peer": true, "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" diff --git a/deploy-web/package.json b/deploy-web/package.json index 7029507cd..f9a7f6962 100644 --- a/deploy-web/package.json +++ b/deploy-web/package.json @@ -17,7 +17,7 @@ "dependencies": { "@akashnetwork/akash-api": "^1.0.3", "@akashnetwork/akashjs": "^0.6.0", - "@auth0/nextjs-auth0": "^3.1.0", + "@auth0/nextjs-auth0": "^3.5.0", "@cosmjs/encoding": "^0.29.5", "@cosmjs/stargate": "^0.29.5", "@cosmos-kit/cosmostation-extension": "^2.6.4", @@ -66,7 +66,7 @@ "@stripe/stripe-js": "^1.41.0", "@tanstack/react-table": "^8.13.2", "@textea/json-viewer": "^3.0.0", - "auth0": "^2.42.0", + "auth0": "^4.3.1", "axios": "^0.27.2", "chain-registry": "^1.20.0", "class-variance-authority": "^0.7.0", @@ -76,6 +76,7 @@ "date-fns": "^2.29.3", "eslint-config-next": "^13.4.18", "file-saver": "^2.0.5", + "geist": "^1.3.0", "http-proxy": "^1.18.1", "iconoir-react": "^7.3.0", "jotai": "^2.0.4", @@ -88,7 +89,7 @@ "lucide-react": "^0.292.0", "material-ui-popup-state": "^4.0.2", "nanoid": "^3.3.4", - "next": "^14.1.0", + "next": "^13.4.19", "next-nprogress-bar": "^2.1.2", "next-pwa": "^5.6.0", "next-qrcode": "^2.1.0", diff --git a/deploy-web/public/android-chrome-192x192.png b/deploy-web/public/android-chrome-192x192.png index 89ff2c80a..320772859 100644 Binary files a/deploy-web/public/android-chrome-192x192.png and b/deploy-web/public/android-chrome-192x192.png differ diff --git a/deploy-web/public/android-chrome-384x384.png b/deploy-web/public/android-chrome-384x384.png new file mode 100644 index 000000000..9e7ddbb49 Binary files /dev/null and b/deploy-web/public/android-chrome-384x384.png differ diff --git a/deploy-web/public/apple-touch-icon.png b/deploy-web/public/apple-touch-icon.png index 42a4d54a4..2d658bbd5 100644 Binary files a/deploy-web/public/apple-touch-icon.png and b/deploy-web/public/apple-touch-icon.png differ diff --git a/deploy-web/public/cloudmos-cover.png b/deploy-web/public/cloudmos-cover.png deleted file mode 100644 index 1562a04ba..000000000 Binary files a/deploy-web/public/cloudmos-cover.png and /dev/null differ diff --git a/deploy-web/public/favicon-16x16.png b/deploy-web/public/favicon-16x16.png index 40ac4b99d..6a003aa1f 100644 Binary files a/deploy-web/public/favicon-16x16.png and b/deploy-web/public/favicon-16x16.png differ diff --git a/deploy-web/public/favicon-32x32.png b/deploy-web/public/favicon-32x32.png index 8072bf821..e310557d8 100644 Binary files a/deploy-web/public/favicon-32x32.png and b/deploy-web/public/favicon-32x32.png differ diff --git a/deploy-web/public/favicon.ico b/deploy-web/public/favicon.ico index c62abd231..d1c22ac79 100644 Binary files a/deploy-web/public/favicon.ico and b/deploy-web/public/favicon.ico differ diff --git a/deploy-web/public/manifest.json b/deploy-web/public/manifest.json index 68afe66c9..4cadc66e1 100644 --- a/deploy-web/public/manifest.json +++ b/deploy-web/public/manifest.json @@ -1,6 +1,7 @@ { - "name": "Cloudmos", - "short_name": "Cloudmos", + "name": "Akash Console", + "short_name": "Akash Console", + "description": "Akash Console - The Akash Network offical console.", "icons": [ { "src": "/android-chrome-192x192.png", diff --git a/deploy-web/public/mstile-150x150.png b/deploy-web/public/mstile-150x150.png index 54b38d24c..8d3b6987f 100644 Binary files a/deploy-web/public/mstile-150x150.png and b/deploy-web/public/mstile-150x150.png differ diff --git a/deploy-web/public/safari-pinned-tab.svg b/deploy-web/public/safari-pinned-tab.svg index 06266d56c..7728edc8b 100644 --- a/deploy-web/public/safari-pinned-tab.svg +++ b/deploy-web/public/safari-pinned-tab.svg @@ -2,30 +2,30 @@ Created by potrace 1.14, written by Peter Selinger 2001-2017 - - - - - + diff --git a/deploy-web/src/app/(home)/WelcomePanel.tsx b/deploy-web/src/app/(home)/WelcomePanel.tsx deleted file mode 100644 index ef9328d52..000000000 --- a/deploy-web/src/app/(home)/WelcomePanel.tsx +++ /dev/null @@ -1,81 +0,0 @@ -"use client"; -import { ReactNode, useState } from "react"; -import Link from "next/link"; -import { UrlService } from "@src/utils/urlUtils"; -import { Card, CardContent, CardHeader, CardTitle } from "@src/components/ui/card"; -import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@src/components/ui/collapsible"; -import { Rocket, SearchEngine, Learning, NavArrowDown } from "iconoir-react"; -import { Button } from "@src/components/ui/button"; -import { Avatar, AvatarFallback } from "@src/components/ui/avatar"; -import { cn } from "@src/utils/styleUtils"; - -type Props = { - children?: ReactNode; -}; - -export const WelcomePanel: React.FC = () => { - const [expanded, setExpanded] = useState(true); - - return ( - - - - Welcome to Akash Console! - - - - - - - - -
    -
  • - - - - - - -
    - Getting started with Akash Console - Learn how to deploy your first docker container on Akash in a few click using Console. -
    -
  • - -
  • - - - - - - -
    - Explore the marketplace - Browse through the marketplace of pre-made solutions with categories like blogs, blockchain nodes and more! -
    -
  • - -
  • - - - - - - -
    - - Learn more about Akash - - Want to know about the advantages of using a decentralized cloud compute marketplace? -
    -
  • -
-
-
-
-
- ); -}; diff --git a/deploy-web/src/app/(home)/YourAccount.tsx b/deploy-web/src/app/(home)/YourAccount.tsx deleted file mode 100644 index 793fd53cf..000000000 --- a/deploy-web/src/app/(home)/YourAccount.tsx +++ /dev/null @@ -1,452 +0,0 @@ -import { useEffect, useState } from "react"; -import { ResponsivePie } from "@nivo/pie"; -import { makeStyles } from "tss-react/mui"; -import { Box, Button, Card, CardContent, CardHeader, Chip, CircularProgress, lighten, Typography, useTheme } from "@mui/material"; -import { getAvgCostPerMonth, uaktToAKT } from "@src/utils/priceUtils"; -import { PriceValue } from "../shared/PriceValue"; -import { DeploymentDto, LeaseDto } from "@src/types/deployment"; -import { StatusPill } from "../shared/StatusPill"; -import { LeaseSpecDetail } from "../shared/LeaseSpecDetail"; -import { bytesToShrink } from "@src/utils/unitUtils"; -import { roundDecimal, udenomToDenom } from "@src/utils/mathHelpers"; -import { UrlService } from "@src/utils/urlUtils"; -import Link from "next/link"; -import { FormattedNumber, FormattedPlural } from "react-intl"; -import { useRouter } from "next/router"; -import RocketLaunchIcon from "@mui/icons-material/RocketLaunch"; -import { useWallet } from "@src/context/WalletProvider"; -import { ConnectWallet } from "../shared/ConnectWallet"; -import { Balances } from "@src/types"; -import { ApiProviderList } from "@src/types/provider"; -import { useAtom } from "jotai"; -import sdlStore from "@src/store/sdlStore"; -import { usePricing } from "@src/context/PricingProvider"; -import { uAktDenom } from "@src/utils/constants"; -import { useUsdcDenom } from "@src/hooks/useDenom"; - -const useStyles = makeStyles()(theme => ({ - legendRow: { - display: "flex", - alignItems: "center", - fontSize: ".75rem", - lineHeight: "1.25rem", - transition: "opacity .2s ease", - marginBottom: ".2rem" - }, - legendColor: { - width: "1rem", - height: "1rem", - borderRadius: "1rem" - }, - legendLabel: { - marginLeft: "1rem", - fontWeight: "bold", - width: "90px" - }, - legendValue: { - marginLeft: "1rem", - width: "100px" - }, - title: { - fontSize: "1rem", - fontWeight: "bold", - marginBottom: ".5rem" - } -})); - -type Props = { - balances: Balances; - isLoadingBalances: boolean; - activeDeployments: Array; - leases: Array; - providers: Array; -}; - -export const YourAccount: React.FunctionComponent = ({ balances, isLoadingBalances, activeDeployments, leases, providers }) => { - const { classes } = useStyles(); - const theme = useTheme(); - const router = useRouter(); - const { address } = useWallet(); - const usdcIbcDenom = useUsdcDenom(); - const [selectedDataId, setSelectedDataId] = useState(null); - const [costPerMonth, setCostPerMonth] = useState(null); - const [userProviders, setUserProviders] = useState<{ owner: string; name: string }[]>(null); - const escrowUAktSum = activeDeployments - .filter(x => x.escrowAccount.balance.denom === uAktDenom) - .map(x => x.escrowBalance) - .reduce((a, b) => a + b, 0); - const escrowUsdcSum = activeDeployments - .filter(x => x.escrowAccount.balance.denom === usdcIbcDenom) - .map(x => x.escrowBalance) - .reduce((a, b) => a + b, 0); - const totalUAkt = balances ? balances.balance + escrowUAktSum : 0; - const totalUsdc = balances ? balances.balanceUsdc + escrowUsdcSum : 0; - const hasBalance = balances && totalUAkt !== 0; - const totalCpu = activeDeployments.map(d => d.cpuAmount).reduce((a, b) => a + b, 0); - const totalGpu = activeDeployments.map(d => d.gpuAmount).reduce((a, b) => a + b, 0); - const totalMemory = activeDeployments.map(d => d.memoryAmount).reduce((a, b) => a + b, 0); - const totalStorage = activeDeployments.map(d => d.storageAmount).reduce((a, b) => a + b, 0); - const _ram = bytesToShrink(totalMemory); - const _storage = bytesToShrink(totalStorage); - const [, setDeploySdl] = useAtom(sdlStore.deploySdl); - const { price, isLoaded } = usePricing(); - - const colors = { - balance_akt: "#dd4320", - balance_usdc: "#dd4320", - deployment_akt: theme.palette.success.dark, - deployment_usdc: theme.palette.success.dark - }; - - const getAktData = (balances: Balances, escrowUAktSum: number) => { - return [ - { - id: "balance_akt", - label: "Balance", - denom: uAktDenom, - denomLabel: "AKT", - value: balances.balance, - color: colors.balance_akt - }, - { - id: "deployment_akt", - label: "Deployment", - denom: uAktDenom, - denomLabel: "AKT", - value: escrowUAktSum, - color: colors.deployment_akt - } - ]; - }; - const getUsdcData = (balances: Balances, escrowUsdcSum: number) => { - return [ - { - id: "balance_usdc", - label: "Balance", - denom: usdcIbcDenom, - denomLabel: "USDC", - value: balances.balanceUsdc, - color: colors.balance_usdc - }, - { - id: "deployment_usdc", - label: "Deployment", - denom: usdcIbcDenom, - denomLabel: "USDC", - value: escrowUsdcSum, - color: colors.deployment_usdc - } - ]; - }; - const aktData = balances ? getAktData(balances, escrowUAktSum) : []; - const usdcData = balances ? getUsdcData(balances, escrowUsdcSum) : []; - const filteredAktData = aktData.filter(x => x.value); - const filteredUsdcData = usdcData.filter(x => x.value); - const allData = [...aktData, ...usdcData]; - - useEffect(() => { - if (leases && providers && price && isLoaded) { - const activeLeases = leases.filter(x => x.state === "active"); - const totalCostPerBlock = activeLeases - .map(x => { - switch (x.price.denom) { - case uAktDenom: - return udenomToDenom(x.price.amount, 10) * price; - case usdcIbcDenom: - return udenomToDenom(x.price.amount, 10); - - default: - return 0; - } - }) - .reduce((a, b) => a + b, 0); - - const _userProviders = activeLeases - .map(x => x.provider) - .filter((value, index, array) => array.indexOf(value) === index) - .map(x => { - const provider = providers.find(p => p.owner === x); - return { owner: provider.owner, name: provider.name }; - }); - - setCostPerMonth(getAvgCostPerMonth(totalCostPerBlock)); - setUserProviders(_userProviders); - } - }, [leases, providers, price, isLoaded]); - - const _getColor = bar => getColor(bar.id, selectedDataId); - const getColor = (id, selectedId) => { - if (!selectedId || id === selectedId) { - return colors[id]; - } else { - return theme.palette.mode === "dark" ? theme.palette.grey[900] : "#e0e0e0"; - } - }; - - const onDeployClick = () => { - setDeploySdl(null); - }; - - return ( - - - - - {address && ( - - {isLoadingBalances && !balances && ( - - - - )} - - - - {activeDeployments.length > 0 && } - 0 ? "1rem" : 0 }}> - You have{" "} - - {activeDeployments.length} active{" "} - - - - - - {activeDeployments.length > 0 ? ( - <> - - - Total resources leased - - - - - {!!totalGpu && } - - - - - - - - Total cost - - - - - - - {" "} - / month - - - - - - - Providers - - - - {userProviders?.map(p => ( - - ))} - - - - ) : ( - - )} - - - - {hasBalance && ( - - {filteredAktData.length > 0 && } - {filteredUsdcData.length > 0 && } - - )} - - {balances && ( - setSelectedDataId(null)}> - {allData.map((balance, i) => ( -
setSelectedDataId(balance.id)} - style={{ opacity: !selectedDataId || balance.id === selectedDataId ? 1 : 0.3 }} - > -
-
{balance.label}
-
- {udenomToDenom(balance.value, 2)} {balance.denomLabel} -
- -
- -
-
- ))} - - -
-
Total
-
- {uaktToAKT(totalUAkt, 2)} AKT -
- -
- - - -
- - -
-
-
- {udenomToDenom(totalUsdc, 2)} USDC -
- -
- - - -
- - - -
-
-
- -
- - - -
- - - )} - - - )} - - {!address && } - - - ); -}; - -type BalancePieProps = { - label: string; - data: Array; - getColor: (bar: any) => string; -}; - -const BalancePie: React.FunctionComponent = ({ label, data, getColor }) => { - const theme = useTheme(); - return ( - - { - return `${udenomToDenom(value, 2)} ${label}`; - }} - tooltip={value => ( - - - - {value.datum.label}: {value.datum.formattedValue} - - - )} - enableArcLinkLabels={false} - arcLabelsSkipAngle={10} - theme={{ - background: theme.palette.mode === "dark" ? lighten(theme.palette.background.paper, 0.0525) : theme.palette.background.paper, - textColor: "#fff", - fontSize: 12, - tooltip: { - basic: { - color: theme.palette.mode === "dark" ? theme.palette.primary.contrastText : theme.palette.primary.main - }, - container: { - backgroundColor: theme.palette.mode === "dark" ? theme.palette.primary.main : theme.palette.primary.contrastText - } - } - }} - /> - - ); -}; - diff --git a/deploy-web/src/app/(home)/page.tsx b/deploy-web/src/app/(home)/page.tsx deleted file mode 100644 index f3d4b615b..000000000 --- a/deploy-web/src/app/(home)/page.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { PageContainer } from "@src/components/shared/PageContainer"; -import React from "react"; -import { HomeContainer } from "./HomeContainer"; - -export default function Home() { - return ( - - - - ); -} - -// import Layout from "../components/layout/Layout"; -// import { ReactNode, useEffect, useState } from "react"; -// import React from "react"; -// import { useDeploymentList } from "@src/queries/useDeploymentQuery"; -// import { useLocalNotes } from "@src/context/LocalNoteProvider"; -// import { useSettings } from "@src/context/SettingsProvider"; -// import { useBalances } from "@src/queries/useBalancesQuery"; -// import { useWallet } from "@src/context/WalletProvider"; -// import { YourAccount } from "@src/components/home/YourAccount"; -// import PageContainer from "@src/components/shared/PageContainer"; -// import { useAllLeases } from "@src/queries/useLeaseQuery"; -// import { WelcomePanel } from "@src/components/home/WelcomePanel"; -// import { Box, CircularProgress } from "@mui/material"; -// import { Footer } from "@src/components/layout/Footer"; -// import { useProviderList } from "@src/queries/useProvidersQuery"; - -// type Props = { -// children?: ReactNode; -// }; - -// const IndexPage: React.FunctionComponent = ({}) => { -// const { address, isWalletLoaded } = useWallet(); -// const [activeDeployments, setActiveDeployments] = useState([]); -// const { isFetching: isLoadingDeployments, refetch: getDeployments } = useDeploymentList(address, { -// enabled: false, -// onSuccess: _deployments => { -// setActiveDeployments( -// _deployments -// ? [..._deployments] -// .filter(d => d.state === "active") -// .map(d => { -// const name = getDeploymentName(d.dseq); - -// return { -// ...d, -// name -// }; -// }) -// : [] -// ); -// } -// }); -// const { getDeploymentName } = useLocalNotes(); -// const { settings, isSettingsInit } = useSettings(); -// const { apiEndpoint } = settings; -// const { data: balances, isFetching: isLoadingBalances, refetch: getBalances } = useBalances(address, { enabled: false }); -// const { data: providers, isFetching: isLoadingProviders } = useProviderList(); -// const { data: leases, isFetching: isLoadingLeases, refetch: getLeases } = useAllLeases(address, { enabled: false }); - -// useEffect(() => { -// if (address && isSettingsInit) { -// getBalances(); -// getLeases(); -// } - -// // eslint-disable-next-line react-hooks/exhaustive-deps -// }, [address, isSettingsInit]); - -// useEffect(() => { -// if (isWalletLoaded && isSettingsInit) { -// getDeployments(); -// } -// }, [isSettingsInit, isWalletLoaded, getDeployments, apiEndpoint, address]); - -// return ( -// -// -//
-// -// -// - -// {isSettingsInit && isWalletLoaded ? ( -// -// ) : ( -// -// -// -// )} -//
- -//
-// -// -// ); -// }; - -// export async function getServerSideProps() { -// return { -// props: {} -// //revalidate: 20 -// }; -// } - -// export default IndexPage; diff --git a/deploy-web/src/app/deployments/[dseq]/page.tsx b/deploy-web/src/app/deployments/[dseq]/page.tsx deleted file mode 100644 index e15ec9b46..000000000 --- a/deploy-web/src/app/deployments/[dseq]/page.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Metadata, ResolvingMetadata } from "next"; -import { DeploymentDetail } from "./DeploymentDetail"; - -interface IDeploymentDetailPageProps { - params: { dseq: string }; - searchParams: { [key: string]: string | string[] | undefined }; -} - -export async function generateMetadata({ params: { dseq } }: IDeploymentDetailPageProps, parent: ResolvingMetadata): Promise { - return { - title: `Deployment detail #${dseq}` - }; -} - -const DeploymentDetailPage: React.FunctionComponent = ({ params: { dseq } }) => { - return ; -}; - -export default DeploymentDetailPage; diff --git a/deploy-web/src/app/deployments/page.tsx b/deploy-web/src/app/deployments/page.tsx deleted file mode 100644 index fc3326830..000000000 --- a/deploy-web/src/app/deployments/page.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from "react"; -import { Metadata } from "next"; -import { DeploymentList } from "./DeploymentList"; - -export const metadata: Metadata = { - title: "Deployments" -}; - -export default function Deployments() { - return ; -} diff --git a/deploy-web/src/app/errors.tsx b/deploy-web/src/app/errors.tsx deleted file mode 100644 index f08987cf8..000000000 --- a/deploy-web/src/app/errors.tsx +++ /dev/null @@ -1,86 +0,0 @@ -"use client"; // Error components must be Client Components - -import { useEffect } from "react"; - -export default function Error({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) { - useEffect(() => { - // Log the error to an error reporting service - console.error(error); - }, [error]); - - return ( -
-

Something went wrong!

- -
- ); -} - - -// import Layout from "../components/layout/Layout"; -// import PageContainer from "@src/components/shared/PageContainer"; -// import { Box, Button, Typography, useTheme } from "@mui/material"; -// import { Title } from "@src/components/shared/Title"; -// import { NextSeo } from "next-seo"; -// import { UrlService } from "@src/utils/urlUtils"; -// import Link from "next/link"; -// import ArrowForwardIcon from "@mui/icons-material/ArrowForward"; -// import { NextPage, NextPageContext } from "next"; -// import * as Sentry from "@sentry/nextjs"; - -// type Props = { -// statusCode: number; -// }; - -// const Error: NextPage = ({ statusCode }) => { -// return ( -// -// - -// -// -// {statusCode} - -// - -// <Typography variant="body1">{statusCode ? `An error ${statusCode} occurred on server` : "An error occurred on client"}</Typography> - -// <Box sx={{ paddingTop: "1rem" }}> -// <Button -// href={UrlService.home()} -// component={Link} -// variant="contained" -// color="secondary" -// sx={{ display: "inline-flex", alignItems: "center", textTransform: "initial" }} -// > -// Go to homepage  -// <ArrowForwardIcon fontSize="small" /> -// </Button> -// </Box> -// </Box> -// </PageContainer> -// </Layout> -// ); -// }; - -// Error.getInitialProps = async (context: NextPageContext) => { -// const { res, err } = context; -// const statusCode = res ? res.statusCode : err ? err.statusCode : 404; - -// // In case this is running in a serverless function, await this in order to give Sentry -// // time to send the error before the lambda exits -// await Sentry.captureUnderscoreErrorException(context); - -// // This will contain the status code of the response -// // return Error.getInitialProps({ statusCode }); -// return { statusCode }; -// }; - -// export default Error; diff --git a/deploy-web/src/app/fonts/Satoshi-Variable.ttf b/deploy-web/src/app/fonts/Satoshi-Variable.ttf deleted file mode 100644 index 976e85cb5..000000000 Binary files a/deploy-web/src/app/fonts/Satoshi-Variable.ttf and /dev/null differ diff --git a/deploy-web/src/app/fonts/Satoshi-Variable.woff b/deploy-web/src/app/fonts/Satoshi-Variable.woff deleted file mode 100644 index f8dcd1d60..000000000 Binary files a/deploy-web/src/app/fonts/Satoshi-Variable.woff and /dev/null differ diff --git a/deploy-web/src/app/fonts/Satoshi-Variable.woff2 b/deploy-web/src/app/fonts/Satoshi-Variable.woff2 deleted file mode 100644 index b00e833ed..000000000 Binary files a/deploy-web/src/app/fonts/Satoshi-Variable.woff2 and /dev/null differ diff --git a/deploy-web/src/app/layout.tsx b/deploy-web/src/app/layout.tsx deleted file mode 100644 index e1c5c02cb..000000000 --- a/deploy-web/src/app/layout.tsx +++ /dev/null @@ -1,135 +0,0 @@ -import type { Metadata, Viewport } from "next"; -import "./globals.css"; -import "../styles/index.css"; -import { cookies } from "next/headers"; -import GoogleAnalytics from "@src/components/layout/CustomGoogleAnalytics"; -import localFont from "next/font/local"; -import { customColors } from "@src/utils/colors"; -import { cn } from "@src/utils/styleUtils"; -import Providers from "@src/components/layout/CustomProviders"; -import { Toaster } from "@src/components/ui/toaster"; -import { AppLayoutContainer } from "@src/components/layout/AppLayoutContainer"; -import getConfig from "next/config"; - -const { publicRuntimeConfig } = getConfig(); - -// const inter = Inter({ -// subsets: ["latin"], -// variable: "--font-sans" -// }); - -const satoshi = localFont({ - src: [ - { - path: "./fonts/Satoshi-Variable.woff", - weight: "400", - style: "normal" - }, - { - path: "./fonts/Satoshi-Variable.woff2", - weight: "400", - style: "normal" - }, - { - path: "./fonts/Satoshi-Variable.ttf", - weight: "400", - style: "normal" - } - ] - // variable: "--font-sans" -}); - -export const metadata: Metadata = { - title: "Akash Console", - description: "Akash Console", - metadataBase: new URL("https://console.akash.network"), - openGraph: { - type: "website", - locale: "en_US", - url: "https://console.akash.network/", - siteName: "Akash Console", - description: - "Akash Console is the #1 platform to deploy docker containers on the Akash Network, a decentralized super cloud compute marketplace. Explore, deploy and track all in one place!", - images: [ - { - url: "https://console.akash.network/akash-console.png", - width: 1200, - height: 630, - alt: "Akash Console 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 : "light"; - return theme; -} - -export default function RootLayout({ children }: { children: React.ReactNode }) { - const theme = getTheme() as string; - const version = publicRuntimeConfig?.version; - - return ( - <html lang="en" className={theme} style={{ colorScheme: theme }} suppressHydrationWarning> - <GoogleAnalytics /> - - <body className={cn("bg-background tracking-wide antialiased", satoshi.className)}> - <Providers version={version}> - <Toaster /> - - <AppLayoutContainer version={version}>{children}</AppLayoutContainer> - </Providers> - </body> - </html> - ); -} diff --git a/deploy-web/src/app/manifest.ts b/deploy-web/src/app/manifest.ts deleted file mode 100644 index 73be56e6e..000000000 --- a/deploy-web/src/app/manifest.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { MetadataRoute } from "next"; - -export default function manifest(): MetadataRoute.Manifest { - return { - name: "Akash Console", - short_name: "Akash Console", - description: "Akash Console - The Akash Network offical console.", - start_url: "/", - display: "standalone", - background_color: "#fff", - theme_color: "#fff", - icons: [ - { - src: "/android-chrome-192x192.png", - sizes: "192x192", - type: "image/png" - }, - { - src: "/android-chrome-384x384.png", - sizes: "384x384", - type: "image/png" - } - ] - }; -} diff --git a/deploy-web/src/app/new-deployment/page.tsx b/deploy-web/src/app/new-deployment/page.tsx deleted file mode 100644 index e9a8fc11b..000000000 --- a/deploy-web/src/app/new-deployment/page.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { NewDeploymentContainer } from "./NewDeploymentContainer"; - -// TODO: Title based on the step -// export async function generateMetadata({ params: { snapshot: snapshotUrlParam } }: IGraphProps, parent: ResolvingMetadata): Promise<Metadata> { -// const snapshot = urlParamToSnapshot(snapshotUrlParam as SnapshotsUrlParam); -// const title = getTitle(snapshot as Snapshots); - -// return { -// title: title -// }; -// } - -export default function NewDeploymentPage() { - return <NewDeploymentContainer />; -} diff --git a/deploy-web/src/app/providers/[owner]/edit/page.tsx b/deploy-web/src/app/providers/[owner]/edit/page.tsx deleted file mode 100644 index dee94ca8c..000000000 --- a/deploy-web/src/app/providers/[owner]/edit/page.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { EditProviderContainer } from "./EditProviderContainer"; - -interface IProviderDetailPageProps { - params: { owner: string }; - searchParams: { [key: string]: string | string[] | undefined }; -} - -{ - /* <NextSeo title={`Edit Provider ${owner}`} /> */ -} - -export default async function EditProviderPage({ params: { owner }, searchParams: { network } }: IProviderDetailPageProps) { - return <EditProviderContainer owner={owner} />; -} diff --git a/deploy-web/src/app/providers/[owner]/leases/page.tsx b/deploy-web/src/app/providers/[owner]/leases/page.tsx deleted file mode 100644 index 27f82d5b8..000000000 --- a/deploy-web/src/app/providers/[owner]/leases/page.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { LeaseListContainer } from "./LeaseListContainer"; - -interface IProviderDetailPageProps { - params: { owner: string }; - searchParams: { [key: string]: string | string[] | undefined }; -} - -// TODO SEO - -export default async function ProviderDetailPage({ params: { owner }, searchParams: { network } }: IProviderDetailPageProps) { - return <LeaseListContainer owner={owner} />; -} diff --git a/deploy-web/src/app/providers/[owner]/page.tsx b/deploy-web/src/app/providers/[owner]/page.tsx deleted file mode 100644 index 94d206b08..000000000 --- a/deploy-web/src/app/providers/[owner]/page.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { Metadata, ResolvingMetadata } from "next"; -import { ProviderDetail } from "./ProviderDetail"; -import { getNetworkBaseApiUrl } from "@src/utils/constants"; -import { ApiProviderDetail } from "@src/types/provider"; -import { UrlService } from "@src/utils/urlUtils"; - -interface IProviderDetailPageProps { - params: { owner: string }; - searchParams: { [key: string]: string | string[] | undefined }; -} - -export async function generateMetadata( - { params: { owner }, searchParams: { network } }: IProviderDetailPageProps, - parent: ResolvingMetadata -): Promise<Metadata> { - const response = await fetchProviderData(owner, network as string); - const url = `https://deploy.cloudmos.io${UrlService.providerDetail(owner)}`; - - return { - title: `Provider detail ${response?.name || response?.owner}`, - alternates: { - canonical: url - }, - openGraph: { - url - } - }; -} - -async function fetchProviderData(owner: string, network: string): Promise<ApiProviderDetail> { - const apiUrl = getNetworkBaseApiUrl(network); - const response = await fetch(`${apiUrl}/v1/providers/${owner}`); - - if (!response.ok) { - // This will activate the closest `error.js` Error Boundary - throw new Error("Error fetching block data"); - } - - return response.json(); -} - -export async function ProviderDetailPage({ params: { owner }, searchParams: { network } }: IProviderDetailPageProps) { - const provider = await fetchProviderData(owner, network as string); - - return <ProviderDetail owner={owner} _provider={provider} />; -} - -export default ProviderDetailPage; diff --git a/deploy-web/src/app/providers/[owner]/raw/page.tsx b/deploy-web/src/app/providers/[owner]/raw/page.tsx deleted file mode 100644 index e40c65346..000000000 --- a/deploy-web/src/app/providers/[owner]/raw/page.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { ProviderRawData } from "./ProviderRawData"; - -interface IProviderDetailPageProps { - params: { owner: string }; - searchParams: { [key: string]: string | string[] | undefined }; -} - -export default async function ProviderRawPage({ params: { owner }, searchParams: { network } }: IProviderDetailPageProps) { - return <ProviderRawData owner={owner} />; -} diff --git a/deploy-web/src/app/providers/page.tsx b/deploy-web/src/app/providers/page.tsx deleted file mode 100644 index 159acf830..000000000 --- a/deploy-web/src/app/providers/page.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from "react"; -import { Metadata } from "next"; -import { ProviderList } from "./ProviderList"; - -export const metadata: Metadata = { - title: "Providers" -}; - -{ - /* <CustomNextSeo -title="Providers" -url={`https://deploy.cloudmos.io${UrlService.providers()}`} -description="Explore all the providers available on the Akash Network." -/> */ -} - -export default function Home() { - return <ProviderList />; -} diff --git a/deploy-web/src/app/settings/authorizations/page.tsx b/deploy-web/src/app/settings/authorizations/page.tsx deleted file mode 100644 index 0c9d9da60..000000000 --- a/deploy-web/src/app/settings/authorizations/page.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from "react"; -import { Metadata } from "next"; -import { Authorizations } from "./Authorizations"; - -export const metadata: Metadata = { - title: "Authorizations" -}; - -export default function AuthorizationsPage() { - return <Authorizations />; -} diff --git a/deploy-web/src/app/settings/page.tsx b/deploy-web/src/app/settings/page.tsx deleted file mode 100644 index 2ca340746..000000000 --- a/deploy-web/src/app/settings/page.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { Metadata } from "next"; -import { SettingsContainer } from "./SettingsContainer"; - -export const metadata: Metadata = { - title: "Settings" -}; - -export default function Settings() { - return <SettingsContainer />; -} diff --git a/deploy-web/src/app/templates/[templateId]/TemplateDetail.tsx b/deploy-web/src/app/templates/[templateId]/TemplateDetail.tsx deleted file mode 100644 index 25ccf5b86..000000000 --- a/deploy-web/src/app/templates/[templateId]/TemplateDetail.tsx +++ /dev/null @@ -1,112 +0,0 @@ -"use client"; -import { useState } from "react"; -import { useRouter } from "next/navigation"; -import Link from "next/link"; -import { UrlService } from "@src/utils/urlUtils"; -import ViewPanel from "@src/components/shared/ViewPanel"; -import { DynamicMonacoEditor } from "@src/components/shared/DynamicMonacoEditor"; -import { LinearLoadingSkeleton } from "@src/components/shared/LinearLoadingSkeleton"; -import { PageContainer } from "@src/components/shared/PageContainer"; -import { RouteStepKeys } from "@src/utils/constants"; -import { ApiTemplate } from "@src/types"; -import Markdown from "@src/components/shared/Markdown"; -import { usePreviousRoute } from "@src/hooks/usePreviousRoute"; -import { useTemplates } from "@src/context/TemplatesProvider"; -import { Tabs, TabsList, TabsTrigger } from "@src/components/ui/tabs"; -import { NavArrowLeft, Rocket } from "iconoir-react"; -import { Button, buttonVariants } from "@src/components/ui/button"; -import { cn } from "@src/utils/styleUtils"; -import GitHubIcon from "@mui/icons-material/GitHub"; - -type Props = { - templateId: string; - template: ApiTemplate; -}; - -export const TemplateDetail: React.FunctionComponent<Props> = ({ templateId, template }) => { - const [activeTab, setActiveTab] = useState("README"); - const { getTemplateById, isLoading } = useTemplates(); - const router = useRouter(); - const _template = template || getTemplateById(templateId); - const previousRoute = usePreviousRoute(); - - function handleBackClick() { - if (previousRoute) { - router.back(); - } else { - router.push(UrlService.templates()); - } - } - - function handleOpenGithub() { - window.open(_template.githubUrl, "_blank"); - } - - return ( - <div className="[&>img]:max-w-full"> - <Tabs value={activeTab} onValueChange={setActiveTab}> - <TabsList className="w-full"> - <TabsTrigger value="README" className={cn({ ["font-bold"]: activeTab === "README" })}> - README - </TabsTrigger> - <TabsTrigger value="SDL" className={cn({ ["font-bold"]: activeTab === "SDL" })}> - View SDL - </TabsTrigger> - {_template?.guide && ( - <TabsTrigger value="GUIDE" className={cn({ ["font-bold"]: activeTab === "GUIDE" })}> - Guide - </TabsTrigger> - )} - </TabsList> - <LinearLoadingSkeleton isLoading={isLoading} /> - - <div className="container flex h-full px-4 py-2 sm:pt-8"> - <div className="flex items-center truncate"> - <Button aria-label="back" onClick={handleBackClick} size="icon" variant="ghost"> - <NavArrowLeft /> - </Button> - <div className="text-truncate"> - <h3 className="ml-4 text-xl font-bold sm:text-2xl md:text-3xl">{_template?.name}</h3> - </div> - - <div className="ml-4"> - <Button aria-label="View on github" title="View on Github" onClick={handleOpenGithub} size="icon" variant="ghost"> - <GitHubIcon fontSize="medium" /> - </Button> - </div> - - <Link - className={cn(buttonVariants({ variant: "default" }), "ml-4 md:ml-8")} - href={UrlService.newDeployment({ step: RouteStepKeys.editDeployment, templateId: _template?.id })} - > - Deploy  - <Rocket className="rotate-45" /> - </Link> - </div> - </div> - - {activeTab === "README" && ( - <ViewPanel stickToBottom style={{ overflow: "auto" }}> - <PageContainer> - <Markdown>{_template?.readme}</Markdown> - </PageContainer> - </ViewPanel> - )} - {activeTab === "SDL" && ( - <ViewPanel stickToBottom style={{ overflow: "hidden" }}> - <PageContainer className="h-full"> - <DynamicMonacoEditor height="100%" language="yaml" value={_template?.deploy || ""} options={{ readOnly: true }} /> - </PageContainer> - </ViewPanel> - )} - {activeTab === "GUIDE" && ( - <ViewPanel stickToBottom style={{ overflow: "auto", padding: "1rem" }}> - <PageContainer> - <Markdown>{_template?.guide}</Markdown> - </PageContainer> - </ViewPanel> - )} - </Tabs> - </div> - ); -}; diff --git a/deploy-web/src/app/templates/[templateId]/page.tsx b/deploy-web/src/app/templates/[templateId]/page.tsx deleted file mode 100644 index c2cd98af4..000000000 --- a/deploy-web/src/app/templates/[templateId]/page.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { UrlService } from "@src/utils/urlUtils"; -import { Metadata, ResolvingMetadata } from "next"; -import { TemplateDetail } from "./TemplateDetail"; -import { ApiTemplate, ApiTemplateCategory } from "@src/types"; -import { BASE_API_MAINNET_URL } from "@src/utils/constants"; -import { getShortText } from "@src/hooks/useShortText"; - -interface ITemplateDetailPageProps { - params: { templateId: string }; - searchParams: { [key: string]: string | string[] | undefined }; -} - -export async function generateMetadata({ params: { templateId } }: ITemplateDetailPageProps, parent: ResolvingMetadata): Promise<Metadata> { - const url = `https://deploy.cloudmos.io${UrlService.templates()}`; - const template = await fetchTemplateDetail(templateId); - - return { - title: `Template detail${template ? " " + template?.name : ""}`, - description: getShortText(template.summary || "", 140), - alternates: { - canonical: url - }, - openGraph: { - url - } - }; -} - -async function fetchTemplateDetail(templateId: string): Promise<ApiTemplate> { - const response = await fetch(`${BASE_API_MAINNET_URL}/templates`); - - if (!response.ok) { - // This will activate the closest `error.js` Error Boundary - throw new Error("Error fetching template detail data"); - } - - const data = (await response.json()) as ApiTemplateCategory[]; - const categories = data.filter(x => (x.templates || []).length > 0); - const templates = categories.flatMap(x => x.templates); - const template = templates.find(x => x.id === templateId); - - return template as ApiTemplate; -} - -export default async function TemplateDetailPage({ params: { templateId } }: ITemplateDetailPageProps) { - const template = await fetchTemplateDetail(templateId); - - return <TemplateDetail templateId={templateId} template={template} />; -} diff --git a/deploy-web/src/app/templates/page.tsx b/deploy-web/src/app/templates/page.tsx deleted file mode 100644 index 7e74a4f9c..000000000 --- a/deploy-web/src/app/templates/page.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { UrlService } from "@src/utils/urlUtils"; -import { TemplateGallery } from "./TemplateGallery"; -import { Metadata } from "next"; - -export async function generateMetadata(): Promise<Metadata> { - const url = `https://deploy.cloudmos.io${UrlService.templates()}`; - - return { - title: "Template Gallery", - description: "Explore all the templates made by the community to easily deploy any docker container on the Akash Network.", - alternates: { - canonical: url - }, - openGraph: { - url - } - }; -} -export default function TemplateGalleryPage() { - return <TemplateGallery />; -} diff --git a/deploy-web/src/app/settings/authorizations/AllowanceGrantedRow.tsx b/deploy-web/src/components/authorizations/AllowanceGrantedRow.tsx similarity index 100% rename from deploy-web/src/app/settings/authorizations/AllowanceGrantedRow.tsx rename to deploy-web/src/components/authorizations/AllowanceGrantedRow.tsx diff --git a/deploy-web/src/app/settings/authorizations/AllowanceIssuedRow.tsx b/deploy-web/src/components/authorizations/AllowanceIssuedRow.tsx similarity index 100% rename from deploy-web/src/app/settings/authorizations/AllowanceIssuedRow.tsx rename to deploy-web/src/components/authorizations/AllowanceIssuedRow.tsx diff --git a/deploy-web/src/app/settings/authorizations/AllowanceModal.tsx b/deploy-web/src/components/authorizations/AllowanceModal.tsx similarity index 97% rename from deploy-web/src/app/settings/authorizations/AllowanceModal.tsx rename to deploy-web/src/components/authorizations/AllowanceModal.tsx index 540f69c22..29f2172a3 100644 --- a/deploy-web/src/app/settings/authorizations/AllowanceModal.tsx +++ b/deploy-web/src/components/authorizations/AllowanceModal.tsx @@ -14,9 +14,9 @@ import { FormattedDate } from "react-intl"; import { Popup } from "@src/components/shared/Popup"; import { Alert } from "@src/components/ui/alert"; import { LinkTo } from "@src/components/shared/LinkTo"; -import FormControl from "@mui/material/FormControl/FormControl"; -import TextField from "@mui/material/TextField/TextField"; -import InputAdornment from "@mui/material/InputAdornment/InputAdornment"; +import FormControl from "@mui/material/FormControl"; +import TextField from "@mui/material/TextField"; +import InputAdornment from "@mui/material/InputAdornment"; import { EncodeObject } from "@cosmjs/proto-signing"; type AllowanceFormValues = { diff --git a/deploy-web/src/app/settings/authorizations/Authorizations.tsx b/deploy-web/src/components/authorizations/Authorizations.tsx similarity index 91% rename from deploy-web/src/app/settings/authorizations/Authorizations.tsx rename to deploy-web/src/components/authorizations/Authorizations.tsx index e2c93f71c..261e13a91 100644 --- a/deploy-web/src/app/settings/authorizations/Authorizations.tsx +++ b/deploy-web/src/components/authorizations/Authorizations.tsx @@ -1,26 +1,26 @@ "use client"; -import { PageContainer } from "@src/components/shared/PageContainer"; -import { SettingsLayout, SettingsTabs } from "@src/app/settings/SettingsLayout"; import { Fieldset } from "@src/components/shared/Fieldset"; import { useEffect, useState } from "react"; import { useWallet } from "@src/context/WalletProvider"; -import { CustomTableHeader } from "@src/components/shared/CustomTable"; import { Address } from "@src/components/shared/Address"; -import { GrantModal } from "@src/app/settings/authorizations/GrantModal"; import { AllowanceType, GrantType } from "@src/types/grant"; import { TransactionMessageData } from "@src/utils/TransactionMessageData"; import { useAllowancesGranted, useAllowancesIssued, useGranteeGrants, useGranterGrants } from "@src/queries/useGrantsQuery"; import { Popup } from "@src/components/shared/Popup"; -import { GranterRow } from "@src/app/settings/authorizations/GranterRow"; -import { GranteeRow } from "@src/app/settings/authorizations/GranteeRow"; -import { AllowanceModal } from "@src/app/settings/authorizations/AllowanceModal"; -import { AllowanceIssuedRow } from "@src/app/settings/authorizations/AllowanceIssuedRow"; import { averageBlockTime } from "@src/utils/priceUtils"; -import { AllowanceGrantedRow } from "@src/app/settings/authorizations/AllowanceGrantedRow"; import { Button } from "@src/components/ui/button"; import { Bank } from "iconoir-react"; import Spinner from "@src/components/shared/Spinner"; import { Table, TableBody, TableHead, TableHeader, TableRow } from "@src/components/ui/table"; +import Layout from "../layout/Layout"; +import { SettingsLayout, SettingsTabs } from "../settings/SettingsLayout"; +import { GrantModal } from "./GrantModal"; +import { AllowanceModal } from "./AllowanceModal"; +import { AllowanceGrantedRow } from "./AllowanceGrantedRow"; +import { AllowanceIssuedRow } from "./AllowanceIssuedRow"; +import { GranteeRow } from "./GranteeRow"; +import { GranterRow } from "./GranterRow"; +import { NextSeo } from "next-seo"; type Props = {}; @@ -120,7 +120,9 @@ export const Authorizations: React.FunctionComponent<Props> = ({}) => { } return ( - <PageContainer isLoading={!!isRefreshing || isLoadingAllowancesIssued || isLoadingAllowancesGranted || isLoadingGranteeGrants || isLoadingGranterGrants}> + <Layout isLoading={!!isRefreshing || isLoadingAllowancesIssued || isLoadingAllowancesGranted || isLoadingGranteeGrants || isLoadingGranterGrants}> + <NextSeo title="Settings Authorizations" /> + <SettingsLayout title="Deployment Authorizations" page={SettingsTabs.AUTHORIZATIONS} @@ -220,7 +222,7 @@ export const Authorizations: React.FunctionComponent<Props> = ({}) => { <> {allowancesIssued.length > 0 ? ( <Table> - <CustomTableHeader> + <TableHeader> <TableRow> <TableHead>Type</TableHead> <TableHead>Grantee</TableHead> @@ -228,7 +230,7 @@ export const Authorizations: React.FunctionComponent<Props> = ({}) => { <TableHead className="text-right">Expiration</TableHead> <TableHead className="text-right"></TableHead> </TableRow> - </CustomTableHeader> + </TableHeader> <TableBody> {allowancesIssued.map(allowance => ( @@ -257,14 +259,14 @@ export const Authorizations: React.FunctionComponent<Props> = ({}) => { <> {allowancesGranted.length > 0 ? ( <Table> - <CustomTableHeader> + <TableHeader> <TableRow> <TableHead>Type</TableHead> <TableHead>Grantee</TableHead> <TableHead>Spending Limit</TableHead> <TableHead className="text-right">Expiration</TableHead> </TableRow> - </CustomTableHeader> + </TableHeader> <TableBody> {allowancesGranted.map(allowance => ( @@ -316,6 +318,6 @@ export const Authorizations: React.FunctionComponent<Props> = ({}) => { {showGrantModal && <GrantModal editingGrant={editingGrant} address={address} onClose={onGrantClose} />} {showAllowanceModal && <AllowanceModal editingAllowance={editingAllowance} address={address} onClose={onAllowanceClose} />} </SettingsLayout> - </PageContainer> + </Layout> ); }; diff --git a/deploy-web/src/app/settings/authorizations/GrantModal.tsx b/deploy-web/src/components/authorizations/GrantModal.tsx similarity index 96% rename from deploy-web/src/app/settings/authorizations/GrantModal.tsx rename to deploy-web/src/components/authorizations/GrantModal.tsx index 9c5af31a2..6b92bc6d6 100644 --- a/deploy-web/src/app/settings/authorizations/GrantModal.tsx +++ b/deploy-web/src/components/authorizations/GrantModal.tsx @@ -17,11 +17,11 @@ import { handleDocClick } from "@src/utils/urlUtils"; import { Popup } from "@src/components/shared/Popup"; import { Alert } from "@src/components/ui/alert"; import { LinkTo } from "@src/components/shared/LinkTo"; -import FormControl from "@mui/material/FormControl/FormControl"; -import InputLabel from "@mui/material/InputLabel/InputLabel"; -import Select from "@mui/material/Select/Select"; -import MenuItem from "@mui/material/MenuItem/MenuItem"; -import TextField from "@mui/material/TextField/TextField"; +import FormControl from "@mui/material/FormControl"; +import InputLabel from "@mui/material/InputLabel"; +import Select from "@mui/material/Select"; +import MenuItem from "@mui/material/MenuItem"; +import TextField from "@mui/material/TextField"; type GrantFormValues = { token: string; amount: number; diff --git a/deploy-web/src/app/settings/authorizations/GranteeRow.tsx b/deploy-web/src/components/authorizations/GranteeRow.tsx similarity index 100% rename from deploy-web/src/app/settings/authorizations/GranteeRow.tsx rename to deploy-web/src/components/authorizations/GranteeRow.tsx diff --git a/deploy-web/src/app/settings/authorizations/GranterRow.tsx b/deploy-web/src/components/authorizations/GranterRow.tsx similarity index 100% rename from deploy-web/src/app/settings/authorizations/GranterRow.tsx rename to deploy-web/src/components/authorizations/GranterRow.tsx diff --git a/deploy-web/src/components/deploymentDetail/DeploymentDepositModal.tsx b/deploy-web/src/components/deployments/DeploymentDepositModal.tsx similarity index 98% rename from deploy-web/src/components/deploymentDetail/DeploymentDepositModal.tsx rename to deploy-web/src/components/deployments/DeploymentDepositModal.tsx index 7d0799f6c..2085def33 100644 --- a/deploy-web/src/components/deploymentDetail/DeploymentDepositModal.tsx +++ b/deploy-web/src/components/deployments/DeploymentDepositModal.tsx @@ -1,7 +1,6 @@ "use client"; import { useState, useRef, useEffect, ReactNode } from "react"; import { useForm, Controller } from "react-hook-form"; -import { useSettings } from "../../context/SettingsProvider"; import compareAsc from "date-fns/compareAsc"; import { coinToUDenom, uaktToAKT } from "@src/utils/priceUtils"; import { uAktDenom } from "@src/utils/constants"; @@ -14,7 +13,6 @@ import { denomToUdenom, udenomToDenom } from "@src/utils/mathHelpers"; import { Popup } from "../shared/Popup"; import { useDenomData } from "@src/hooks/useWalletBalance"; import { useUsdcDenom } from "@src/hooks/useDenom"; -import { GranteeDepositMenuItem } from "../../app/deployments/[dseq]/GranteeDepositMenuItem"; import { useToast } from "../ui/use-toast"; import { Alert } from "../ui/alert"; import { FormItem } from "../ui/form"; @@ -23,6 +21,8 @@ import { InputWithIcon } from "../ui/input"; import { Checkbox } from "../ui/checkbox"; import { Label } from "../ui/label"; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "../ui/select"; +import { useSettings } from "@src/context/SettingsProvider"; +import { GranteeDepositMenuItem } from "./GranteeDepositMenuItem"; type Props = { infoText?: string | ReactNode; diff --git a/deploy-web/src/app/deployments/[dseq]/DeploymentDetail.tsx b/deploy-web/src/components/deployments/DeploymentDetail.tsx similarity index 57% rename from deploy-web/src/app/deployments/[dseq]/DeploymentDetail.tsx rename to deploy-web/src/components/deployments/DeploymentDetail.tsx index 3337afe69..96efc3cf3 100644 --- a/deploy-web/src/app/deployments/[dseq]/DeploymentDetail.tsx +++ b/deploy-web/src/components/deployments/DeploymentDetail.tsx @@ -25,23 +25,10 @@ import { ArrowRight } from "iconoir-react"; import { ManifestUpdate } from "./ManifestUpdate"; import { LeaseRow } from "./LeaseRow"; import { DeploymentLeaseShell } from "./DeploymentLeaseShell"; - -// const useStyles = makeStyles()(theme => ({ -// tabsRoot: { -// minHeight: "36px", -// borderBottom: `1px solid ${theme.palette.mode === "dark" ? theme.palette.grey[900] : theme.palette.grey[300]}`, -// backgroundColor: theme.palette.mode === "dark" ? theme.palette.grey[900] : theme.palette.grey[200], -// "& button": { -// minHeight: "36px" -// } -// }, -// selectedTab: { -// fontWeight: "bold" -// } -// })); +import Layout from "../layout/Layout"; +import { NextSeo } from "next-seo"; export function DeploymentDetail({ dseq }: React.PropsWithChildren<{ dseq: string }>) { - // const { classes } = useStyles(); const router = useRouter(); const [activeTab, setActiveTab] = useState("LEASES"); const [selectedLogsMode, setSelectedLogsMode] = useState<LOGS_MODE>("logs"); @@ -145,38 +132,38 @@ export function DeploymentDetail({ dseq }: React.PropsWithChildren<{ dseq: strin }; return ( - <PageContainer isLoading={isLoadingLeases || isLoadingDeployment || isLoadingProviders} isUsingSettings isUsingWallet> - {/* <Layout isLoading={isLoadingLeases || isLoadingDeployment || isLoadingProviders} isUsingSettings isUsingWallet> */} - - {/* <PageContainer sx={{ padding: "1rem 0 0" }}> */} - {deployment && ( - <DeploymentDetailTopBar - address={address} - loadDeploymentDetail={loadDeploymentDetail} - removeLeases={removeLeases} - setActiveTab={setActiveTab} - deployment={deployment} - /> - )} - - {isDeploymentNotFound && ( - <div className="mt-8 text-center"> - <h1>404</h1> - <h5>This deployment does not exist or it was created using another wallet.</h5> - <div className="pt-4"> - <Link href={UrlService.home()} className={cn(buttonVariants({ variant: "default" }), "inline-flex items-center")}> - Go to homepage  - <ArrowRight fontSize="small" /> - </Link> + <Layout isLoading={isLoadingLeases || isLoadingDeployment || isLoadingProviders} isUsingSettings isUsingWallet> + <NextSeo title={`Deployment detail #${dseq}`} /> + + <PageContainer className="pt-4"> + {deployment && ( + <DeploymentDetailTopBar + address={address} + loadDeploymentDetail={loadDeploymentDetail} + removeLeases={removeLeases} + setActiveTab={setActiveTab} + deployment={deployment} + /> + )} + + {isDeploymentNotFound && ( + <div className="mt-8 text-center"> + <h1>404</h1> + <h5>This deployment does not exist or it was created using another wallet.</h5> + <div className="pt-4"> + <Link href={UrlService.home()} className={cn(buttonVariants({ variant: "default" }), "inline-flex items-center")}> + Go to homepage  + <ArrowRight fontSize="small" /> + </Link> + </div> </div> - </div> - )} + )} - {deployment && ( - <> - <DeploymentSubHeader deployment={deployment} leases={leases} /> + {deployment && ( + <> + <DeploymentSubHeader deployment={deployment} leases={leases} /> - {/* <Tabs + {/* <Tabs value={activeTab} onChange={onChangeTab} indicatorColor="secondary" @@ -193,67 +180,68 @@ export function DeploymentDetail({ dseq }: React.PropsWithChildren<{ dseq: strin <Tab value="EDIT" label="Update" classes={{ selected: classes.selectedTab }} /> </Tabs> */} - <Tabs value={activeTab} onValueChange={onChangeTab}> - <TabsList className="mb-4 grid w-full grid-cols-4"> - <TabsTrigger value="LEASES">Leases</TabsTrigger> - {isActive && <TabsTrigger value="LOGS">Logs</TabsTrigger>} - {isActive && <TabsTrigger value="SHELL">Shell</TabsTrigger>} - {isActive && <TabsTrigger value="EVENTS">Events</TabsTrigger>} - <TabsTrigger value="EDIT">Update</TabsTrigger> - </TabsList> - - {activeTab === "EDIT" && deployment && leases && ( - <ManifestUpdate - deployment={deployment} - leases={leases} - closeManifestEditor={() => { - setActiveTab("EVENTS"); - setSelectedLogsMode("events"); - loadDeploymentDetail(); - }} - /> - )} - {activeTab === "LOGS" && <DeploymentLogs leases={leases} selectedLogsMode="logs" />} - {activeTab === "EVENTS" && <DeploymentLogs leases={leases} selectedLogsMode="events" />} - {activeTab === "SHELL" && <DeploymentLeaseShell leases={leases} />} - {activeTab === "LEASES" && ( - <div className="p-4"> - {leases && (!localCert || !isLocalCertMatching) && ( - <div className="mb-4"> - <Alert variant="warning">You do not have a valid local certificate. You need to create a new one to view lease status and details.</Alert> - - <Button className="mt-4" disabled={isCreatingCert} onClick={() => createCertificate()}> - {isCreatingCert ? <Spinner size="medium" /> : "Create Certificate"} - </Button> - </div> - )} - - {leases && - leases.map((lease, i) => ( - <LeaseRow - key={lease.id} - lease={lease} - setActiveTab={setActiveTab} - ref={leaseRefs[i]} - deploymentManifest={deploymentManifest || ""} - dseq={dseq} - providers={providers || []} - loadDeploymentDetail={loadDeploymentDetail} - /> - ))} - - {!hasLeases && !isLoadingLeases && !isLoadingDeployment && <>This deployment doesn't have any leases</>} - - {(isLoadingLeases || isLoadingDeployment) && !hasLeases && ( - <div className="flex items-center justify-center p-8"> - <Spinner size="large" /> - </div> - )} - </div> - )} - </Tabs> - </> - )} - </PageContainer> + <Tabs value={activeTab} onValueChange={onChangeTab}> + <TabsList className="mb-4 grid w-full grid-cols-4"> + <TabsTrigger value="LEASES">Leases</TabsTrigger> + {isActive && <TabsTrigger value="LOGS">Logs</TabsTrigger>} + {isActive && <TabsTrigger value="SHELL">Shell</TabsTrigger>} + {isActive && <TabsTrigger value="EVENTS">Events</TabsTrigger>} + <TabsTrigger value="EDIT">Update</TabsTrigger> + </TabsList> + + {activeTab === "EDIT" && deployment && leases && ( + <ManifestUpdate + deployment={deployment} + leases={leases} + closeManifestEditor={() => { + setActiveTab("EVENTS"); + setSelectedLogsMode("events"); + loadDeploymentDetail(); + }} + /> + )} + {activeTab === "LOGS" && <DeploymentLogs leases={leases} selectedLogsMode="logs" />} + {activeTab === "EVENTS" && <DeploymentLogs leases={leases} selectedLogsMode="events" />} + {activeTab === "SHELL" && <DeploymentLeaseShell leases={leases} />} + {activeTab === "LEASES" && ( + <div className="p-4"> + {leases && (!localCert || !isLocalCertMatching) && ( + <div className="mb-4"> + <Alert variant="warning">You do not have a valid local certificate. You need to create a new one to view lease status and details.</Alert> + + <Button className="mt-4" disabled={isCreatingCert} onClick={() => createCertificate()}> + {isCreatingCert ? <Spinner size="medium" /> : "Create Certificate"} + </Button> + </div> + )} + + {leases && + leases.map((lease, i) => ( + <LeaseRow + key={lease.id} + lease={lease} + setActiveTab={setActiveTab} + ref={leaseRefs[i]} + deploymentManifest={deploymentManifest || ""} + dseq={dseq} + providers={providers || []} + loadDeploymentDetail={loadDeploymentDetail} + /> + ))} + + {!hasLeases && !isLoadingLeases && !isLoadingDeployment && <>This deployment doesn't have any leases</>} + + {(isLoadingLeases || isLoadingDeployment) && !hasLeases && ( + <div className="flex items-center justify-center p-8"> + <Spinner size="large" /> + </div> + )} + </div> + )} + </Tabs> + </> + )} + </PageContainer> + </Layout> ); } diff --git a/deploy-web/src/app/deployments/[dseq]/DeploymentDetailTopBar.tsx b/deploy-web/src/components/deployments/DeploymentDetailTopBar.tsx similarity index 98% rename from deploy-web/src/app/deployments/[dseq]/DeploymentDetailTopBar.tsx rename to deploy-web/src/components/deployments/DeploymentDetailTopBar.tsx index 5dd2d7464..f1b9b1a67 100644 --- a/deploy-web/src/app/deployments/[dseq]/DeploymentDetailTopBar.tsx +++ b/deploy-web/src/components/deployments/DeploymentDetailTopBar.tsx @@ -1,6 +1,6 @@ "use client"; import { Dispatch, ReactNode, SetStateAction, useState } from "react"; -import { DeploymentDepositModal } from "../../../components/deploymentDetail/DeploymentDepositModal"; +import { DeploymentDepositModal } from "./DeploymentDepositModal"; import { useLocalNotes } from "@src/context/LocalNoteProvider"; import { useRouter } from "next/navigation"; import { UrlService } from "@src/utils/urlUtils"; diff --git a/deploy-web/src/app/deployments/[dseq]/DeploymentLeaseShell.tsx b/deploy-web/src/components/deployments/DeploymentLeaseShell.tsx similarity index 100% rename from deploy-web/src/app/deployments/[dseq]/DeploymentLeaseShell.tsx rename to deploy-web/src/components/deployments/DeploymentLeaseShell.tsx diff --git a/deploy-web/src/app/deployments/DeploymentList.tsx b/deploy-web/src/components/deployments/DeploymentList.tsx similarity index 92% rename from deploy-web/src/app/deployments/DeploymentList.tsx rename to deploy-web/src/components/deployments/DeploymentList.tsx index c652093ed..baa9d2fcf 100644 --- a/deploy-web/src/app/deployments/DeploymentList.tsx +++ b/deploy-web/src/components/deployments/DeploymentList.tsx @@ -1,11 +1,10 @@ "use client"; import { NextSeo } from "next-seo"; import { useWallet } from "@src/context/WalletProvider"; -import { useAddressDeployments, useDeploymentList } from "@src/queries/useDeploymentQuery"; +import { useDeploymentList } from "@src/queries/useDeploymentQuery"; import { useEffect, useState } from "react"; import { useSettings } from "@src/context/SettingsProvider"; import { useLocalNotes } from "@src/context/LocalNoteProvider"; -import { DeploymentListRow } from "@src/app/deployments/DeploymentListRow"; import Link from "next/link"; import { UrlService } from "@src/utils/urlUtils"; import { TransactionMessageData } from "@src/utils/TransactionMessageData"; @@ -24,30 +23,11 @@ import { IconButton } from "@mui/material"; import Spinner from "@src/components/shared/Spinner"; import { CustomPagination } from "@src/components/shared/CustomPagination"; import { Table, TableBody, TableHead, TableHeader, TableRow } from "@src/components/ui/table"; +import Layout from "../layout/Layout"; +import { DeploymentListRow } from "./DeploymentListRow"; type Props = {}; -// const useStyles = makeStyles()(theme => ({ -// root: { -// "& .MuiPagination-ul": { -// justifyContent: "center" -// } -// }, -// titleContainer: { -// paddingBottom: "0.5rem", -// display: "flex", -// alignItems: "center", -// flexWrap: "wrap" -// }, -// title: { -// fontSize: "1.5rem", -// fontWeight: "bold" -// }, -// createBtn: { -// marginLeft: "auto" -// } -// })); - export const DeploymentList: React.FunctionComponent<Props> = ({}) => { const { address, signAndBroadcastTx, isWalletLoaded } = useWallet(); const { data: providers, isFetching: isLoadingProviders } = useProviderList(); @@ -142,8 +122,10 @@ export const DeploymentList: React.FunctionComponent<Props> = ({}) => { }; return ( - <PageContainer isLoading={isLoadingDeployments || isLoadingProviders} isUsingSettings isUsingWallet> - <div> + <Layout isLoading={isLoadingDeployments || isLoadingProviders} isUsingSettings isUsingWallet> + <NextSeo title="Deployments" /> + + <PageContainer> <div className="flex flex-wrap items-center pb-2"> <h3 className="text-2xl font-bold">Deployments</h3> @@ -299,11 +281,11 @@ export const DeploymentList: React.FunctionComponent<Props> = ({}) => { )} {(filteredDeployments?.length || 0) > 0 && ( - <div className="pb-8 pt-4 flex items-center justify-center"> + <div className="flex items-center justify-center pb-8 pt-4"> <CustomPagination totalPageCount={pageCount} setPageIndex={handleChangePage} pageIndex={pageIndex} pageSize={pageSize} setPageSize={setPageSize} /> </div> )} - </div> - </PageContainer> + </PageContainer> + </Layout> ); }; diff --git a/deploy-web/src/app/deployments/DeploymentListRow.tsx b/deploy-web/src/components/deployments/DeploymentListRow.tsx similarity index 94% rename from deploy-web/src/app/deployments/DeploymentListRow.tsx rename to deploy-web/src/components/deployments/DeploymentListRow.tsx index d94f7582a..6cc79fb3f 100644 --- a/deploy-web/src/app/deployments/DeploymentListRow.tsx +++ b/deploy-web/src/components/deployments/DeploymentListRow.tsx @@ -5,31 +5,31 @@ import { LeaseChip } from "./LeaseChip"; import formatDistanceToNow from "date-fns/formatDistanceToNow"; import isValid from "date-fns/isValid"; import differenceInCalendarDays from "date-fns/differenceInCalendarDays"; -import { SpecDetailList } from "../../components/shared/SpecDetailList"; +import { SpecDetailList } from "../shared/SpecDetailList"; import { useAllLeases } from "@src/queries/useLeaseQuery"; import { useRouter } from "next/navigation"; import { getAvgCostPerMonth, getTimeLeft, useRealTimeLeft } from "@src/utils/priceUtils"; import { UrlService } from "@src/utils/urlUtils"; -import { DeploymentDepositModal } from "../../components/deploymentDetail/DeploymentDepositModal"; +import { DeploymentDepositModal } from "./DeploymentDepositModal"; import { useWallet } from "@src/context/WalletProvider"; import { TransactionMessageData } from "@src/utils/TransactionMessageData"; import { event } from "nextjs-google-analytics"; import { AnalyticsEvents } from "@src/utils/analytics"; import { NamedDeploymentDto } from "@src/types/deployment"; import { ApiProviderList } from "@src/types/provider"; -import { PriceValue } from "../../components/shared/PriceValue"; -import { CustomTooltip } from "../../components/shared/CustomTooltip"; -import { PricePerMonth } from "../../components/shared/PricePerMonth"; +import { PriceValue } from "../shared/PriceValue"; +import { CustomTooltip } from "../shared/CustomTooltip"; +import { PricePerMonth } from "../shared/PricePerMonth"; import { udenomToDenom } from "@src/utils/mathHelpers"; import { useDenomData } from "@src/hooks/useWalletBalance"; -import { TableCell, TableRow } from "../../components/ui/table"; +import { TableCell, TableRow } from "../ui/table"; import { WarningCircle, MoreHoriz, InfoCircle, NavArrowRight, Plus, Edit, XmarkSquare, Upload } from "iconoir-react"; -import { CustomDropdownLinkItem } from "../../components/shared/CustomDropdownLinkItem"; -import { Button } from "../../components/ui/button"; -import { Checkbox } from "../../components/ui/checkbox"; -import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from "../../components/ui/dropdown-menu"; -import Spinner from "../../components/shared/Spinner"; -import ClickAwayListener from "react-click-away-listener"; +import { CustomDropdownLinkItem } from "../shared/CustomDropdownLinkItem"; +import { Button } from "../ui/button"; +import { Checkbox } from "../ui/checkbox"; +import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from "../ui/dropdown-menu"; +import Spinner from "../shared/Spinner"; +import ClickAwayListener from "@mui/material/ClickAwayListener"; // const useStyles = makeStyles()(theme => ({ // root: { @@ -98,7 +98,6 @@ export const DeploymentListRow: React.FunctionComponent<Props> = ({ deployment, const router = useRouter(); const [open, setOpen] = useState(false); const [isDepositingDeployment, setIsDepositingDeployment] = useState(false); - const [anchorEl, setAnchorEl] = useState(null); const { changeDeploymentName, getDeploymentData } = useLocalNotes(); const { address, signAndBroadcastTx } = useWallet(); const isActive = deployment.state === "active"; @@ -133,12 +132,12 @@ export const DeploymentListRow: React.FunctionComponent<Props> = ({ deployment, function handleMenuClick(ev) { ev.stopPropagation(); - setAnchorEl(ev.currentTarget); + setOpen(true); } const handleMenuClose = (event?) => { event?.stopPropagation(); - setAnchorEl(null); + setOpen(false); }; const onDeploymentDeposit = async (deposit, depositorAddress) => { diff --git a/deploy-web/src/app/deployments/[dseq]/DeploymentLogs.tsx b/deploy-web/src/components/deployments/DeploymentLogs.tsx similarity index 100% rename from deploy-web/src/app/deployments/[dseq]/DeploymentLogs.tsx rename to deploy-web/src/components/deployments/DeploymentLogs.tsx diff --git a/deploy-web/src/app/deployments/[dseq]/DeploymentSubHeader.tsx b/deploy-web/src/components/deployments/DeploymentSubHeader.tsx similarity index 100% rename from deploy-web/src/app/deployments/[dseq]/DeploymentSubHeader.tsx rename to deploy-web/src/components/deployments/DeploymentSubHeader.tsx diff --git a/deploy-web/src/app/deployments/[dseq]/GranteeDepositMenuItem.tsx b/deploy-web/src/components/deployments/GranteeDepositMenuItem.tsx similarity index 86% rename from deploy-web/src/app/deployments/[dseq]/GranteeDepositMenuItem.tsx rename to deploy-web/src/components/deployments/GranteeDepositMenuItem.tsx index c8293422e..b3866ff98 100644 --- a/deploy-web/src/app/deployments/[dseq]/GranteeDepositMenuItem.tsx +++ b/deploy-web/src/components/deployments/GranteeDepositMenuItem.tsx @@ -1,10 +1,10 @@ "use client"; import React, { ReactNode } from "react"; -import { Address } from "../../../components/shared/Address"; +import { Address } from "../shared/Address"; import { FormattedDate } from "react-intl"; import { coinToUDenom } from "@src/utils/priceUtils"; import { GrantType } from "@src/types/grant"; -import { AKTAmount } from "../../../components/shared/AKTAmount"; +import { AKTAmount } from "../shared/AKTAmount"; import { useDenomData } from "@src/hooks/useWalletBalance"; type Props = { diff --git a/deploy-web/src/app/deployments/LeaseChip.tsx b/deploy-web/src/components/deployments/LeaseChip.tsx similarity index 92% rename from deploy-web/src/app/deployments/LeaseChip.tsx rename to deploy-web/src/components/deployments/LeaseChip.tsx index f597a64d3..46f2d6ed1 100644 --- a/deploy-web/src/app/deployments/LeaseChip.tsx +++ b/deploy-web/src/components/deployments/LeaseChip.tsx @@ -1,11 +1,11 @@ "use client"; import { useEffect, useState } from "react"; -import { StatusPill } from "../../components/shared/StatusPill"; +import { StatusPill } from "../shared/StatusPill"; import Link from "next/link"; import { UrlService } from "@src/utils/urlUtils"; import { LeaseDto } from "@src/types/deployment"; import { ApiProviderList } from "@src/types/provider"; -import { Badge } from "../../components/ui/badge"; +import { Badge } from "../ui/badge"; // const useStyles = makeStyles()(theme => ({ // leaseChip: { diff --git a/deploy-web/src/app/deployments/[dseq]/LeaseRow.tsx b/deploy-web/src/components/deployments/LeaseRow.tsx similarity index 99% rename from deploy-web/src/app/deployments/[dseq]/LeaseRow.tsx rename to deploy-web/src/components/deployments/LeaseRow.tsx index 07a5a475f..9e843a307 100644 --- a/deploy-web/src/app/deployments/[dseq]/LeaseRow.tsx +++ b/deploy-web/src/components/deployments/LeaseRow.tsx @@ -9,7 +9,6 @@ import { getGpusFromAttributes, sendManifestToProvider } from "@src/utils/deploy import Link from "next/link"; import { UrlService } from "@src/utils/urlUtils"; import { copyTextToClipboard } from "@src/utils/copyClipboard"; -import { cx } from "@emotion/css"; import { getSplitText } from "@src/hooks/useShortText"; import { ApiProviderList } from "@src/types/provider"; import { LeaseDto } from "@src/types/deployment"; diff --git a/deploy-web/src/app/deployments/[dseq]/LeaseSelect.tsx b/deploy-web/src/components/deployments/LeaseSelect.tsx similarity index 100% rename from deploy-web/src/app/deployments/[dseq]/LeaseSelect.tsx rename to deploy-web/src/components/deployments/LeaseSelect.tsx diff --git a/deploy-web/src/app/deployments/[dseq]/ManifestUpdate.tsx b/deploy-web/src/components/deployments/ManifestUpdate.tsx similarity index 100% rename from deploy-web/src/app/deployments/[dseq]/ManifestUpdate.tsx rename to deploy-web/src/components/deployments/ManifestUpdate.tsx diff --git a/deploy-web/src/app/deployments/[dseq]/ServiceSelect.tsx b/deploy-web/src/components/deployments/ServiceSelect.tsx similarity index 100% rename from deploy-web/src/app/deployments/[dseq]/ServiceSelect.tsx rename to deploy-web/src/components/deployments/ServiceSelect.tsx diff --git a/deploy-web/src/app/deployments/[dseq]/ShellDownloadModal.tsx b/deploy-web/src/components/deployments/ShellDownloadModal.tsx similarity index 100% rename from deploy-web/src/app/deployments/[dseq]/ShellDownloadModal.tsx rename to deploy-web/src/components/deployments/ShellDownloadModal.tsx diff --git a/deploy-web/src/app/(home)/HomeContainer.tsx b/deploy-web/src/components/home/HomeContainer.tsx similarity index 97% rename from deploy-web/src/app/(home)/HomeContainer.tsx rename to deploy-web/src/components/home/HomeContainer.tsx index 7fc4c4087..a4b0714e2 100644 --- a/deploy-web/src/app/(home)/HomeContainer.tsx +++ b/deploy-web/src/components/home/HomeContainer.tsx @@ -12,6 +12,7 @@ import { Footer } from "@src/components/layout/Footer"; import { useProviderList } from "@src/queries/useProvidersQuery"; import { DeploymentDto } from "@src/types/deployment"; import { WelcomePanel } from "./WelcomePanel"; +import Layout from "../layout/Layout"; export function HomeContainer() { const { address, isWalletLoaded } = useWallet(); @@ -43,7 +44,7 @@ export function HomeContainer() { const { data: leases, isFetching: isLoadingLeases, refetch: getLeases } = useAllLeases(address, { enabled: false }); return ( - <div> + <Layout> <Box sx={{ marginBottom: "1rem" }}> <WelcomePanel /> </Box> @@ -54,6 +55,6 @@ export function HomeContainer() { <CircularProgress color="secondary" size="4rem" /> </Box> )} */} - </div> + </Layout> ); } diff --git a/deploy-web/src/components/home/WelcomePanel.tsx b/deploy-web/src/components/home/WelcomePanel.tsx index f0f364e26..ef9328d52 100644 --- a/deploy-web/src/components/home/WelcomePanel.tsx +++ b/deploy-web/src/components/home/WelcomePanel.tsx @@ -1,11 +1,13 @@ -import { Card, CardContent, CardHeader, Collapse, IconButton, List, ListItem, ListItemAvatar, ListItemText, useTheme } from "@mui/material"; +"use client"; import { ReactNode, useState } from "react"; -import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; -import RocketLaunchIcon from "@mui/icons-material/RocketLaunch"; -import CategoryIcon from "@mui/icons-material/Category"; -import SchoolIcon from "@mui/icons-material/School"; import Link from "next/link"; import { UrlService } from "@src/utils/urlUtils"; +import { Card, CardContent, CardHeader, CardTitle } from "@src/components/ui/card"; +import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@src/components/ui/collapsible"; +import { Rocket, SearchEngine, Learning, NavArrowDown } from "iconoir-react"; +import { Button } from "@src/components/ui/button"; +import { Avatar, AvatarFallback } from "@src/components/ui/avatar"; +import { cn } from "@src/utils/styleUtils"; type Props = { children?: ReactNode; @@ -13,58 +15,67 @@ type Props = { export const WelcomePanel: React.FC<Props> = () => { const [expanded, setExpanded] = useState(true); - const theme = useTheme(); return ( - <Card elevation={1}> - <CardHeader - title="Welcome to Cloudmos!" - titleTypographyProps={{ variant: "h3", sx: { fontSize: "1.25rem", fontWeight: "bold" } }} - sx={{ borderBottom: expanded ? `1px solid ${theme.palette.mode === "dark" ? theme.palette.grey[800] : theme.palette.grey[200]}` : "" }} - action={ - <IconButton onClick={() => setExpanded(prev => !prev)}> - <ExpandMoreIcon /> - </IconButton> - } - /> + <Collapsible open={expanded} onOpenChange={setExpanded}> + <Card> + <CardHeader className="flex flex-row items-center justify-between"> + <CardTitle className="text-2xl font-bold">Welcome to Akash Console!</CardTitle> - <Collapse in={expanded} timeout="auto" unmountOnExit> - <CardContent sx={{ padding: "0 !important" }}> - <List> - <ListItem> - <ListItemAvatar> - <RocketLaunchIcon fontSize="large" /> - </ListItemAvatar> - <ListItemText - primary={<Link href={UrlService.getStarted()}>Getting started with Cloudmos</Link>} - secondary="Learn how to deploy your first docker container on Akash in a few click using Cloudmos." - /> - </ListItem> - <ListItem> - <ListItemAvatar> - <CategoryIcon fontSize="large" /> - </ListItemAvatar> - <ListItemText - primary={<Link href={UrlService.templates()}>Explore the marketplace</Link>} - secondary="Browse through the marketplace of pre-made solutions with categories like blogs, blockchain nodes and more!" - /> - </ListItem> - <ListItem> - <ListItemAvatar> - <SchoolIcon fontSize="large" /> - </ListItemAvatar> - <ListItemText - primary={ - <a target="_blank" rel="noopener noreferrer" href="https://docs.akash.network/"> + <CollapsibleTrigger asChild> + <Button size="icon" variant="ghost" className="!m-0 rounded-full" onClick={() => setExpanded(prev => !prev)}> + <NavArrowDown fontSize="1rem" className={cn("transition-all duration-100", { ["rotate-180"]: expanded })} /> + </Button> + </CollapsibleTrigger> + </CardHeader> + + <CollapsibleContent> + <CardContent> + <ul className="space-y-4"> + <li className="flex items-center"> + <Avatar className="h-12 w-12"> + <AvatarFallback> + <Rocket /> + </AvatarFallback> + </Avatar> + + <div className="ml-4 flex flex-col"> + <Link href={UrlService.getStarted()}>Getting started with Akash Console</Link> + <span>Learn how to deploy your first docker container on Akash in a few click using Console.</span> + </div> + </li> + + <li className="flex items-center"> + <Avatar className="h-12 w-12"> + <AvatarFallback> + <SearchEngine /> + </AvatarFallback> + </Avatar> + + <div className="ml-4 flex flex-col"> + <Link href={UrlService.templates()}>Explore the marketplace</Link> + <span>Browse through the marketplace of pre-made solutions with categories like blogs, blockchain nodes and more!</span> + </div> + </li> + + <li className="flex items-center"> + <Avatar className="h-12 w-12"> + <AvatarFallback> + <Learning /> + </AvatarFallback> + </Avatar> + + <div className="ml-4 flex flex-col"> + <Link href="https://docs.akash.network/" target="_blank"> Learn more about Akash - </a> - } - secondary="Want to know about the advantages of using a decentralized cloud compute marketplace?" - /> - </ListItem> - </List> - </CardContent> - </Collapse> - </Card> + </Link> + <span>Want to know about the advantages of using a decentralized cloud compute marketplace?</span> + </div> + </li> + </ul> + </CardContent> + </CollapsibleContent> + </Card> + </Collapsible> ); }; diff --git a/deploy-web/src/components/icons/AkashConsoleLogo.tsx b/deploy-web/src/components/icons/AkashConsoleLogo.tsx index ac7b87a77..773b7db43 100644 --- a/deploy-web/src/components/icons/AkashConsoleLogo.tsx +++ b/deploy-web/src/components/icons/AkashConsoleLogo.tsx @@ -1,45 +1,117 @@ -export const AkashConsoleLightLogo = ({ className = "" }: { className?: string }) => { +export const AkashConsoleLogoDark = ({ className = "" }: { className?: string }) => { return ( - <svg xmlns="http://www.w3.org/2000/svg" className={className} width="775" height="102" viewBox="0 0 775 102" fill="none"> - <path d="M76.535 67.1227L95.4329 100.361H57.2594L38.1621 67.1227H76.535Z" fill="url(#paint0_linear_2769_50)" /> - <path d="M95.4226 100.366L114.488 67.1283L76.3253 0.635986H38.1621L95.4226 100.366Z" fill="#FF414C" /> - <path d="M19.0815 33.8654H57.2447L19.0973 100.358L0 67.1195L19.0815 33.8654Z" fill="#FF414C" /> + <svg xmlns="http://www.w3.org/2000/svg" width="170" height="19" viewBox="0 0 170 19" fill="none" className={className}> + <path d="M13.7518 12.0425L17.1474 18.0147H10.2884L6.85693 12.0425H13.7518Z" fill="url(#paint0_linear_868_9890)" /> + <path d="M17.1455 18.0158L20.5713 12.0436L13.7141 0.0961914H6.85693L17.1455 18.0158Z" fill="#FF414C" /> + <path d="M3.42857 6.06689H10.2858L3.43142 18.0143L0 12.042L3.42857 6.06689Z" fill="#FF414C" /> <path - d="M192.614 32.3816L191.655 42.8014C187.542 34.7124 178.084 30.3251 167.94 30.3251C148.885 30.3251 136.137 44.1724 136.137 65.4232C136.137 86.537 147.652 101.892 167.802 101.892C178.769 101.892 186.857 96.5454 191.106 89.5532L192.203 100.11H207.83V32.3816H192.614ZM190.832 66.2459C190.832 78.4479 183.293 86.8112 171.641 86.8112C159.989 86.8112 152.861 78.3108 152.861 66.2459C152.861 54.1809 160.126 45.5434 171.778 45.5434C183.43 45.5434 190.832 54.0438 190.832 66.2459Z" - fill="black" + d="M35.1479 5.80026L34.9755 7.67249C34.2366 6.21905 32.537 5.43074 30.7143 5.43074C27.2907 5.43074 25 7.91884 25 11.7372C25 15.5309 27.069 18.29 30.6897 18.29C32.6602 18.29 34.1134 17.3293 34.877 16.0729L35.074 17.9698H37.8819V5.80026H35.1479ZM34.8277 11.885C34.8277 14.0775 33.473 15.5802 31.3794 15.5802C29.2858 15.5802 28.005 14.0528 28.005 11.885C28.005 9.71716 29.3104 8.16518 31.404 8.16518C33.4976 8.16518 34.8277 9.69253 34.8277 11.885Z" + fill="white" + /> + <path + d="M42.9947 17.9698V14.6933L44.7681 12.8458L47.9455 17.9698H51.3938L46.8617 10.6286L51.4923 5.80026H47.7238L42.9947 10.8996V0H40.0144V17.9698H42.9947Z" + fill="white" + /> + <path + d="M61.4851 5.80026L61.3127 7.67249C60.5737 6.21905 58.8742 5.43074 57.0515 5.43074C53.6279 5.43074 51.3372 7.91884 51.3372 11.7372C51.3372 15.5309 53.4062 18.29 57.0269 18.29C58.9974 18.29 60.4506 17.3293 61.2141 16.0729L61.4112 17.9698H64.2191V5.80026H61.4851ZM61.1649 11.885C61.1649 14.0775 59.8102 15.5802 57.7166 15.5802C55.6229 15.5802 54.3421 14.0528 54.3421 11.885C54.3421 9.71716 55.6476 8.16518 57.7412 8.16518C59.8348 8.16518 61.1649 9.69253 61.1649 11.885Z" + fill="white" + /> + <path + d="M65.7397 14.2746C65.7397 16.6641 67.6609 18.29 70.6905 18.29C73.6955 18.29 75.7891 16.7627 75.7891 14.2992C75.7891 12.427 74.7546 11.4909 72.7102 11.0228L70.5181 10.5055C69.4836 10.2591 68.9417 9.8157 68.9417 9.12593C68.9417 8.21445 69.6314 7.67249 70.8136 7.67249C71.9713 7.67249 72.661 8.33762 72.6856 9.37228H75.5428C75.5181 7.00735 73.6708 5.43074 70.9368 5.43074C68.1289 5.43074 66.1092 6.85955 66.1092 9.2491C66.1092 11.1952 67.1683 12.2299 69.3604 12.7226L71.5526 13.2399C72.6363 13.4863 72.9565 13.9297 72.9565 14.5455C72.9565 15.4324 72.193 15.999 70.789 15.999C69.4097 15.999 68.6215 15.3338 68.5969 14.2746H65.7397Z" + fill="white" + /> + <path + d="M80.4681 17.9698V11.3184C80.4681 9.49545 81.5765 8.16518 83.4731 8.16518C85.0002 8.16518 86.01 9.15056 86.01 11.1952V17.9698H89.015V10.4808C89.015 7.3276 87.4386 5.43074 84.4337 5.43074C82.6849 5.43074 81.2563 6.19441 80.4927 7.37687V0H77.4631V17.9698H80.4681Z" + fill="white" + /> + <path + d="M92 12.0452C92 15.797 94.247 18.29 97.557 18.29C100.215 18.29 102.293 16.6856 102.848 14.242H101.157C100.698 15.7723 99.2966 16.7597 97.557 16.7597C95.1892 16.7597 93.6429 14.8591 93.6429 12.0206C93.6429 9.08329 95.31 7.23208 97.6778 7.23208C99.2966 7.23208 100.698 8.14534 101.133 9.7991H102.8C102.341 7.33081 100.36 5.72642 97.6536 5.72642C94.247 5.72642 92 8.29344 92 12.0452Z" + fill="#A3A3A3" + /> + <path + d="M104.069 12.0206C104.069 15.6489 106.582 18.29 110.013 18.29C113.444 18.29 115.956 15.6489 115.956 12.0206C115.956 8.36749 113.444 5.72642 110.013 5.72642C106.582 5.72642 104.069 8.36749 104.069 12.0206ZM105.76 11.9959C105.76 9.23139 107.5 7.25676 110.013 7.25676C112.501 7.25676 114.265 9.23139 114.265 11.9959C114.265 14.8097 112.501 16.7597 110.013 16.7597C107.5 16.7597 105.76 14.8097 105.76 11.9959Z" + fill="#A3A3A3" + /> + <path + d="M119.807 17.9938V11.749C119.807 9.05861 121.16 7.28144 123.551 7.28144C125.484 7.28144 126.717 8.26876 126.717 11.0579V17.9938H128.384V10.6877C128.384 7.67637 127.055 5.72642 123.841 5.72642C122.15 5.72642 120.604 6.59032 119.831 8.12066L119.589 6.0473H118.139V17.9938H119.807Z" + fill="#A3A3A3" + /> + <path + d="M130.091 14.6369C130.091 16.8584 131.734 18.29 134.367 18.29C137.025 18.29 138.813 16.8831 138.813 14.7357C138.813 13.0079 137.895 12.0452 135.865 11.5269L133.86 11.0086C132.58 10.6877 131.951 10.0459 131.951 9.13266C131.951 7.84915 132.869 7.13335 134.512 7.13335C136.083 7.13335 137.025 7.94788 137.073 9.33012H138.692C138.62 7.10866 137.025 5.72642 134.561 5.72642C132.048 5.72642 130.333 7.0593 130.333 9.15734C130.333 10.7864 131.323 11.8725 133.353 12.3908L135.358 12.9091C136.735 13.2547 137.218 13.8224 137.218 14.8097C137.218 16.0932 136.155 16.8831 134.343 16.8831C132.7 16.8831 131.686 16.0192 131.686 14.6369H130.091Z" + fill="#A3A3A3" + /> + <path + d="M140.253 12.0206C140.253 15.6489 142.766 18.29 146.196 18.29C149.627 18.29 152.14 15.6489 152.14 12.0206C152.14 8.36749 149.627 5.72642 146.196 5.72642C142.766 5.72642 140.253 8.36749 140.253 12.0206ZM141.944 11.9959C141.944 9.23139 143.684 7.25676 146.196 7.25676C148.685 7.25676 150.449 9.23139 150.449 11.9959C150.449 14.8097 148.685 16.7597 146.196 16.7597C143.684 16.7597 141.944 14.8097 141.944 11.9959Z" + fill="#A3A3A3" + /> + <path d="M156.063 17.9938V0H154.396V17.9938H156.063Z" fill="#A3A3A3" /> + <path + d="M164.03 18.29C166.784 18.29 168.596 16.9324 169.224 14.4148H167.63C167.195 15.9945 165.963 16.8337 164.054 16.8337C161.541 16.8337 160.043 15.1553 159.923 12.2427H169.224V11.4035C169.224 8.02193 167.074 5.72642 163.909 5.72642C160.575 5.72642 158.304 8.29344 158.304 12.0206C158.304 15.7723 160.599 18.29 164.03 18.29ZM163.909 7.18271C166.108 7.18271 167.557 8.71305 167.557 11.0086H159.971C160.261 8.66368 161.71 7.18271 163.909 7.18271Z" + fill="#A3A3A3" + /> + <defs> + <linearGradient id="paint0_linear_868_9890" x1="12" y1="16.5" x2="16.0069" y2="12.0061" gradientUnits="userSpaceOnUse"> + <stop stopColor="#FF414C" /> + <stop offset="1" stopColor="#FF414C" stopOpacity="0" /> + </linearGradient> + </defs> + </svg> + ); +}; + +export const AkashConsoleLogoLight = ({ className = "" }: { className?: string }) => { + return ( + <svg width="170" height="19" viewBox="0 0 170 19" fill="none" xmlns="http://www.w3.org/2000/svg" className={className}> + <path d="M13.7518 12.0425L17.1474 18.0147H10.2884L6.85693 12.0425H13.7518Z" fill="url(#paint0_linear_868_9889)" /> + <path d="M17.1455 18.0158L20.5713 12.0436L13.7141 0.0961914H6.85693L17.1455 18.0158Z" fill="#FF414C" /> + <path d="M3.42857 6.06689H10.2858L3.43142 18.0143L0 12.042L3.42857 6.06689Z" fill="#FF414C" /> + <path + d="M35.1479 5.80026L34.9755 7.67249C34.2366 6.21905 32.537 5.43074 30.7143 5.43074C27.2907 5.43074 25 7.91884 25 11.7372C25 15.5309 27.069 18.29 30.6897 18.29C32.6602 18.29 34.1134 17.3293 34.877 16.0729L35.074 17.9698H37.8819V5.80026H35.1479ZM34.8277 11.885C34.8277 14.0775 33.473 15.5802 31.3794 15.5802C29.2858 15.5802 28.005 14.0528 28.005 11.885C28.005 9.71716 29.3104 8.16518 31.404 8.16518C33.4976 8.16518 34.8277 9.69253 34.8277 11.885Z" + fill="#232323" /> <path - d="M236.285 100.11V81.8755L246.155 71.5928L263.838 100.11H283.03L257.807 59.2537L283.578 32.3816H262.605L236.285 60.7618V0.100708H219.698V100.11H236.285Z" - fill="black" + d="M42.9947 17.9698V14.6933L44.7681 12.8458L47.9455 17.9698H51.3938L46.8617 10.6286L51.4923 5.80026H47.7238L42.9947 10.8996V0H40.0144V17.9698H42.9947Z" + fill="#232323" /> <path - d="M339.192 32.3816L338.232 42.8014C334.12 34.7124 324.661 30.3251 314.517 30.3251C295.463 30.3251 282.714 44.1724 282.714 65.4232C282.714 86.537 294.229 101.892 314.38 101.892C325.347 101.892 333.434 96.5454 337.684 89.5532L338.781 100.11H354.408V32.3816H339.192ZM337.41 66.2459C337.41 78.4479 329.87 86.8112 318.218 86.8112C306.567 86.8112 299.438 78.3108 299.438 66.2459C299.438 54.1809 306.704 45.5434 318.355 45.5434C330.007 45.5434 337.41 54.0438 337.41 66.2459Z" - fill="black" + d="M61.4851 5.80026L61.3127 7.67249C60.5737 6.21905 58.8742 5.43074 57.0515 5.43074C53.6279 5.43074 51.3372 7.91884 51.3372 11.7372C51.3372 15.5309 53.4062 18.29 57.0269 18.29C58.9974 18.29 60.4506 17.3293 61.2141 16.0729L61.4112 17.9698H64.2191V5.80026H61.4851ZM61.1649 11.885C61.1649 14.0775 59.8102 15.5802 57.7166 15.5802C55.6229 15.5802 54.3421 14.0528 54.3421 11.885C54.3421 9.71716 55.6476 8.16518 57.7412 8.16518C59.8348 8.16518 61.1649 9.69253 61.1649 11.885Z" + fill="#232323" /> <path - d="M362.871 79.5448C362.871 92.8437 373.563 101.892 390.424 101.892C407.148 101.892 418.8 93.3921 418.8 79.6819C418.8 69.2621 413.042 64.0522 401.665 61.4473L389.464 58.5681C383.707 57.1971 380.691 54.7293 380.691 50.8904C380.691 45.8176 384.529 42.8014 391.109 42.8014C397.552 42.8014 401.39 46.5032 401.527 52.2614H417.429C417.292 39.0996 407.011 30.3251 391.795 30.3251C376.167 30.3251 364.927 38.277 364.927 51.5759C364.927 62.407 370.821 68.1653 383.021 70.9073L395.222 73.7865C401.253 75.1575 403.035 77.6253 403.035 81.0529C403.035 85.9886 398.786 89.1419 390.972 89.1419C383.296 89.1419 378.909 85.4402 378.772 79.5448H362.871Z" - fill="black" + d="M65.7397 14.2746C65.7397 16.6641 67.6609 18.29 70.6905 18.29C73.6955 18.29 75.7891 16.7627 75.7891 14.2992C75.7891 12.427 74.7546 11.4909 72.7102 11.0228L70.5181 10.5055C69.4836 10.2591 68.9417 9.8157 68.9417 9.12593C68.9417 8.21445 69.6314 7.67249 70.8136 7.67249C71.9713 7.67249 72.661 8.33762 72.6856 9.37228H75.5428C75.5181 7.00735 73.6708 5.43074 70.9368 5.43074C68.1289 5.43074 66.1092 6.85955 66.1092 9.2491C66.1092 11.1952 67.1683 12.2299 69.3604 12.7226L71.5526 13.2399C72.6363 13.4863 72.9565 13.9297 72.9565 14.5455C72.9565 15.4324 72.193 15.999 70.789 15.999C69.4097 15.999 68.6215 15.3338 68.5969 14.2746H65.7397Z" + fill="#232323" /> <path - d="M444.84 100.11V63.0925C444.84 52.947 451.009 45.5434 461.564 45.5434C470.063 45.5434 475.684 51.0275 475.684 62.407V100.11H492.408V58.431C492.408 40.882 483.634 30.3251 466.91 30.3251C457.178 30.3251 449.227 34.5753 444.978 41.1562V0.100708H428.117V100.11H444.84Z" - fill="black" + d="M80.4681 17.9698V11.3184C80.4681 9.49545 81.5765 8.16518 83.4731 8.16518C85.0002 8.16518 86.01 9.15056 86.01 11.1952V17.9698H89.015V10.4808C89.015 7.3276 87.4386 5.43074 84.4337 5.43074C82.6849 5.43074 81.2563 6.19441 80.4927 7.37687V0H77.4631V17.9698H80.4681Z" + fill="#232323" + /> + <path + d="M92 12.0452C92 15.797 94.247 18.29 97.557 18.29C100.215 18.29 102.293 16.6856 102.848 14.242H101.157C100.698 15.7723 99.2966 16.7597 97.557 16.7597C95.1892 16.7597 93.6429 14.8591 93.6429 12.0206C93.6429 9.08329 95.31 7.23208 97.6778 7.23208C99.2966 7.23208 100.698 8.14534 101.133 9.7991H102.8C102.341 7.33081 100.36 5.72642 97.6536 5.72642C94.247 5.72642 92 8.29344 92 12.0452Z" + fill="#A3A3A3" + /> + <path + d="M104.069 12.0206C104.069 15.6489 106.582 18.29 110.013 18.29C113.444 18.29 115.956 15.6489 115.956 12.0206C115.956 8.36749 113.444 5.72642 110.013 5.72642C106.582 5.72642 104.069 8.36749 104.069 12.0206ZM105.76 11.9959C105.76 9.23139 107.5 7.25676 110.013 7.25676C112.501 7.25676 114.265 9.23139 114.265 11.9959C114.265 14.8097 112.501 16.7597 110.013 16.7597C107.5 16.7597 105.76 14.8097 105.76 11.9959Z" + fill="#A3A3A3" + /> + <path + d="M119.807 17.9938V11.749C119.807 9.05861 121.16 7.28144 123.551 7.28144C125.484 7.28144 126.717 8.26876 126.717 11.0579V17.9938H128.384V10.6877C128.384 7.67637 127.055 5.72642 123.841 5.72642C122.15 5.72642 120.604 6.59032 119.831 8.12066L119.589 6.0473H118.139V17.9938H119.807Z" + fill="#A3A3A3" /> <path - d="M724.029 80.9852H733.257C733.257 88.8148 739.129 93.7084 748.637 93.7084C759.123 93.7084 765.275 89.2343 765.275 81.9639C765.275 76.3713 762.478 73.1556 754.509 71.1982L742.904 68.2621C731.16 65.326 725.427 59.1741 725.427 49.9463C725.427 38.0621 735.354 30.5121 749.895 30.5121C764.156 30.5121 773.384 38.3417 773.803 50.925H764.436C764.156 43.0954 758.703 38.4815 749.615 38.4815C740.108 38.4815 734.795 42.5362 734.795 49.8065C734.795 54.9797 738.43 58.6149 745.84 60.4324L757.445 63.3685C769.189 66.3047 774.502 71.7574 774.502 81.5445C774.502 93.7084 764.156 101.678 748.777 101.678C733.537 101.678 724.029 93.5685 724.029 80.9852Z" + d="M130.091 14.6369C130.091 16.8584 131.734 18.29 134.367 18.29C137.025 18.29 138.813 16.8831 138.813 14.7357C138.813 13.0079 137.895 12.0452 135.865 11.5269L133.86 11.0086C132.58 10.6877 131.951 10.0459 131.951 9.13266C131.951 7.84915 132.869 7.13335 134.512 7.13335C136.083 7.13335 137.025 7.94788 137.073 9.33012H138.692C138.62 7.10866 137.025 5.72642 134.561 5.72642C132.048 5.72642 130.333 7.0593 130.333 9.15734C130.333 10.7864 131.323 11.8725 133.353 12.3908L135.358 12.9091C136.735 13.2547 137.218 13.8224 137.218 14.8097C137.218 16.0932 136.155 16.8831 134.343 16.8831C132.7 16.8831 131.686 16.0192 131.686 14.6369H130.091Z" fill="#A3A3A3" /> - <path d="M705.034 100H695.527V40.4389H681.965V32.3296H695.527V10.938H705.034V32.3296H718.596V40.4389H705.034V100Z" fill="#A3A3A3" /> <path - d="M662.264 32.3297H670.792V100H661.984L661.005 86.1584C656.112 96.3648 646.884 101.678 635.419 101.678C615.426 101.678 604.101 86.2982 604.101 65.8852C604.101 45.4723 616.264 30.5121 635.839 30.5121C646.744 30.5121 656.671 35.2658 661.425 46.451L662.264 32.3297ZM660.586 66.1648C660.586 50.5056 652.197 39.3204 637.376 39.3204C622.556 39.3204 613.888 50.5056 613.888 66.1648C613.888 81.8241 622.416 93.0093 637.097 93.0093C651.917 93.0093 660.586 81.8241 660.586 66.1648Z" + d="M140.253 12.0206C140.253 15.6489 142.766 18.29 146.196 18.29C149.627 18.29 152.14 15.6489 152.14 12.0206C152.14 8.36749 149.627 5.72642 146.196 5.72642C142.766 5.72642 140.253 8.36749 140.253 12.0206ZM141.944 11.9959C141.944 9.23139 143.684 7.25676 146.196 7.25676C148.685 7.25676 150.449 9.23139 150.449 11.9959C150.449 14.8097 148.685 16.7597 146.196 16.7597C143.684 16.7597 141.944 14.8097 141.944 11.9959Z" fill="#A3A3A3" /> - <path d="M584.811 100H575.303V40.4389H561.741V32.3296H575.303V10.938H584.811V32.3296H598.373V40.4389H584.811V100Z" fill="#A3A3A3" /> + <path d="M156.063 17.9938V0H154.396V17.9938H156.063Z" fill="#A3A3A3" /> <path - d="M504.334 80.9852H513.562C513.562 88.8148 519.434 93.7084 528.941 93.7084C539.427 93.7084 545.579 89.2343 545.579 81.9639C545.579 76.3713 542.783 73.1556 534.814 71.1982L523.209 68.2621C511.465 65.326 505.732 59.1741 505.732 49.9463C505.732 38.0621 515.659 30.5121 530.2 30.5121C544.461 30.5121 553.689 38.3417 554.108 50.925H544.74C544.461 43.0954 539.008 38.4815 529.92 38.4815C520.413 38.4815 515.1 42.5362 515.1 49.8065C515.1 54.9797 518.735 58.6149 526.145 60.4324L537.75 63.3685C549.494 66.3047 554.807 71.7574 554.807 81.5445C554.807 93.7084 544.461 101.678 529.081 101.678C513.841 101.678 504.334 93.5685 504.334 80.9852Z" + d="M164.03 18.29C166.784 18.29 168.596 16.9324 169.224 14.4148H167.63C167.195 15.9945 165.963 16.8337 164.054 16.8337C161.541 16.8337 160.043 15.1553 159.923 12.2427H169.224V11.4035C169.224 8.02193 167.074 5.72642 163.909 5.72642C160.575 5.72642 158.304 8.29344 158.304 12.0206C158.304 15.7723 160.599 18.29 164.03 18.29ZM163.909 7.18271C166.108 7.18271 167.557 8.71305 167.557 11.0086H159.971C160.261 8.66368 161.71 7.18271 163.909 7.18271Z" fill="#A3A3A3" /> <defs> - <linearGradient id="paint0_linear_2769_50" x1="66.7855" y1="91.9307" x2="89.0854" y2="66.9203" gradientUnits="userSpaceOnUse"> + <linearGradient id="paint0_linear_868_9889" x1="12" y1="16.5" x2="16.0069" y2="12.0061" gradientUnits="userSpaceOnUse"> <stop stopColor="#FF414C" /> <stop offset="1" stopColor="#FF414C" stopOpacity="0" /> </linearGradient> @@ -48,48 +120,130 @@ export const AkashConsoleLightLogo = ({ className = "" }: { className?: string } ); }; -export const AkashConsoleDarkLogo = ({ className = "" }: { className?: string }) => { +export const AkashConsoleBetaLogoDark = ({ className = "" }: { className?: string }) => { return ( - <svg xmlns="http://www.w3.org/2000/svg" className={className} width="775" height="102" viewBox="0 0 775 102" fill="none"> - <path d="M76.535 67.1228L95.4329 100.361H57.2594L38.1621 67.1228H76.535Z" fill="url(#paint0_linear_2769_41)" /> - <path d="M95.4226 100.367L114.488 67.1284L76.3253 0.636078H38.1621L95.4226 100.367Z" fill="#FF414C" /> - <path d="M19.0815 33.8655H57.2447L19.0973 100.358L0 67.1196L19.0815 33.8655Z" fill="#FF414C" /> + <svg xmlns="http://www.w3.org/2000/svg" width="198" height="20" viewBox="0 0 198 20" fill="none" className={className}> + <path d="M13.7518 13.0425L17.1474 19.0147H10.2884L6.85693 13.0425H13.7518Z" fill="url(#paint0_linear_882_9888)" /> + <path d="M17.1455 19.0158L20.5713 13.0436L13.7141 1.09619H6.85693L17.1455 19.0158Z" fill="#FF414C" /> + <path d="M3.42857 7.06689H10.2858L3.43142 19.0143L0 13.042L3.42857 7.06689Z" fill="#FF414C" /> <path - d="M192.614 32.3817L191.655 42.8014C187.542 34.7124 178.084 30.3251 167.94 30.3251C148.885 30.3251 136.137 44.1725 136.137 65.4233C136.137 86.537 147.652 101.892 167.802 101.892C178.769 101.892 186.857 96.5455 191.106 89.5532L192.203 100.11H207.83V32.3817H192.614ZM190.832 66.2459C190.832 78.448 183.293 86.8112 171.641 86.8112C159.989 86.8112 152.861 78.3109 152.861 66.2459C152.861 54.1809 160.126 45.5435 171.778 45.5435C183.43 45.5435 190.832 54.0438 190.832 66.2459Z" + d="M35.1479 6.80026L34.9755 8.67249C34.2366 7.21905 32.537 6.43074 30.7143 6.43074C27.2907 6.43074 25 8.91884 25 12.7372C25 16.5309 27.069 19.29 30.6897 19.29C32.6602 19.29 34.1134 18.3293 34.877 17.0729L35.074 18.9698H37.8819V6.80026H35.1479ZM34.8277 12.885C34.8277 15.0775 33.473 16.5802 31.3794 16.5802C29.2858 16.5802 28.005 15.0528 28.005 12.885C28.005 10.7172 29.3104 9.16518 31.404 9.16518C33.4976 9.16518 34.8277 10.6925 34.8277 12.885Z" fill="white" /> <path - d="M236.285 100.11V81.8755L246.155 71.5929L263.838 100.11H283.03L257.807 59.2537L283.578 32.3817H262.605L236.285 60.7618V0.100739H219.698V100.11H236.285Z" + d="M42.9947 18.9698V15.6933L44.7681 13.8458L47.9455 18.9698H51.3938L46.8617 11.6286L51.4923 6.80026H47.7238L42.9947 11.8996V1H40.0144V18.9698H42.9947Z" fill="white" /> <path - d="M339.192 32.3817L338.232 42.8014C334.12 34.7124 324.661 30.3251 314.517 30.3251C295.463 30.3251 282.714 44.1725 282.714 65.4233C282.714 86.537 294.229 101.892 314.38 101.892C325.347 101.892 333.434 96.5455 337.684 89.5532L338.781 100.11H354.408V32.3817H339.192ZM337.41 66.2459C337.41 78.448 329.87 86.8112 318.218 86.8112C306.567 86.8112 299.438 78.3109 299.438 66.2459C299.438 54.1809 306.704 45.5435 318.355 45.5435C330.007 45.5435 337.41 54.0438 337.41 66.2459Z" + d="M61.4851 6.80026L61.3127 8.67249C60.5737 7.21905 58.8742 6.43074 57.0515 6.43074C53.6279 6.43074 51.3372 8.91884 51.3372 12.7372C51.3372 16.5309 53.4062 19.29 57.0269 19.29C58.9974 19.29 60.4506 18.3293 61.2141 17.0729L61.4112 18.9698H64.2191V6.80026H61.4851ZM61.1649 12.885C61.1649 15.0775 59.8102 16.5802 57.7166 16.5802C55.6229 16.5802 54.3421 15.0528 54.3421 12.885C54.3421 10.7172 55.6476 9.16518 57.7412 9.16518C59.8348 9.16518 61.1649 10.6925 61.1649 12.885Z" fill="white" /> <path - d="M362.871 79.5448C362.871 92.8437 373.563 101.892 390.424 101.892C407.148 101.892 418.8 93.3921 418.8 79.6819C418.8 69.2621 413.042 64.0523 401.665 61.4473L389.464 58.5682C383.707 57.1971 380.691 54.7293 380.691 50.8905C380.691 45.8177 384.529 42.8014 391.109 42.8014C397.552 42.8014 401.39 46.5032 401.527 52.2615H417.429C417.292 39.0997 407.011 30.3251 391.795 30.3251C376.167 30.3251 364.927 38.2771 364.927 51.576C364.927 62.407 370.821 68.1653 383.021 70.9074L395.222 73.7865C401.253 75.1575 403.035 77.6254 403.035 81.0529C403.035 85.9886 398.786 89.1419 390.972 89.1419C383.296 89.1419 378.909 85.4402 378.772 79.5448H362.871Z" + d="M65.7397 15.2746C65.7397 17.6641 67.6609 19.29 70.6905 19.29C73.6954 19.29 75.7891 17.7627 75.7891 15.2992C75.7891 13.427 74.7546 12.4909 72.7102 12.0228L70.5181 11.5055C69.4836 11.2591 68.9417 10.8157 68.9417 10.1259C68.9417 9.21445 69.6314 8.67249 70.8136 8.67249C71.9713 8.67249 72.661 9.33762 72.6856 10.3723H75.5428C75.5181 8.00735 73.6708 6.43074 70.9368 6.43074C68.1289 6.43074 66.1092 7.85955 66.1092 10.2491C66.1092 12.1952 67.1683 13.2299 69.3604 13.7226L71.5526 14.2399C72.6363 14.4863 72.9565 14.9297 72.9565 15.5455C72.9565 16.4324 72.193 16.999 70.789 16.999C69.4097 16.999 68.6215 16.3338 68.5969 15.2746H65.7397Z" fill="white" /> <path - d="M444.84 100.11V63.0925C444.84 52.947 451.009 45.5435 461.564 45.5435C470.063 45.5435 475.684 51.0276 475.684 62.407V100.11H492.408V58.4311C492.408 40.882 483.634 30.3251 466.91 30.3251C457.178 30.3251 449.227 34.5753 444.978 41.1562V0.100739H428.117V100.11H444.84Z" + d="M80.4681 18.9698V12.3184C80.4681 10.4955 81.5765 9.16518 83.4731 9.16518C85.0002 9.16518 86.01 10.1506 86.01 12.1952V18.9698H89.015V11.4808C89.015 8.3276 87.4386 6.43074 84.4337 6.43074C82.6849 6.43074 81.2563 7.19441 80.4927 8.37687V1H77.4632V18.9698H80.4681Z" fill="white" /> <path - d="M724.029 80.9852H733.257C733.257 88.8148 739.129 93.7083 748.637 93.7083C759.123 93.7083 765.275 89.2343 765.275 81.9639C765.275 76.3713 762.478 73.1556 754.509 71.1982L742.904 68.2621C731.16 65.3259 725.427 59.1741 725.427 49.9463C725.427 38.0621 735.354 30.5121 749.895 30.5121C764.156 30.5121 773.384 38.3417 773.803 50.925H764.436C764.156 43.0954 758.703 38.4815 749.615 38.4815C740.108 38.4815 734.795 42.5361 734.795 49.8065C734.795 54.9796 738.43 58.6148 745.84 60.4324L757.445 63.3685C769.189 66.3046 774.502 71.7574 774.502 81.5445C774.502 93.7083 764.156 101.678 748.777 101.678C733.537 101.678 724.029 93.5685 724.029 80.9852Z" + d="M92 13.0452C92 16.797 94.247 19.29 97.557 19.29C100.215 19.29 102.293 17.6856 102.848 15.242H101.157C100.698 16.7723 99.2966 17.7597 97.557 17.7597C95.1892 17.7597 93.6429 15.8591 93.6429 13.0206C93.6429 10.0833 95.31 8.23208 97.6778 8.23208C99.2966 8.23208 100.698 9.14534 101.133 10.7991H102.8C102.341 8.33081 100.36 6.72642 97.6536 6.72642C94.247 6.72642 92 9.29344 92 13.0452Z" fill="#A3A3A3" /> - <path d="M705.034 100H695.527V40.4389H681.965V32.3297H695.527V10.938H705.034V32.3297H718.596V40.4389H705.034V100Z" fill="#A3A3A3" /> <path - d="M662.264 32.3297H670.792V100H661.984L661.005 86.1583C656.112 96.3648 646.884 101.678 635.419 101.678C615.426 101.678 604.101 86.2982 604.101 65.8852C604.101 45.4722 616.264 30.5121 635.839 30.5121C646.744 30.5121 656.671 35.2658 661.425 46.451L662.264 32.3297ZM660.586 66.1648C660.586 50.5056 652.197 39.3204 637.376 39.3204C622.556 39.3204 613.888 50.5056 613.888 66.1648C613.888 81.8241 622.416 93.0093 637.097 93.0093C651.917 93.0093 660.586 81.8241 660.586 66.1648Z" + d="M104.069 13.0206C104.069 16.6489 106.582 19.29 110.013 19.29C113.444 19.29 115.956 16.6489 115.956 13.0206C115.956 9.36749 113.444 6.72642 110.013 6.72642C106.582 6.72642 104.069 9.36749 104.069 13.0206ZM105.76 12.9959C105.76 10.2314 107.5 8.25676 110.013 8.25676C112.501 8.25676 114.265 10.2314 114.265 12.9959C114.265 15.8097 112.501 17.7597 110.013 17.7597C107.5 17.7597 105.76 15.8097 105.76 12.9959Z" fill="#A3A3A3" /> - <path d="M584.811 100H575.303V40.4389H561.741V32.3297H575.303V10.938H584.811V32.3297H598.373V40.4389H584.811V100Z" fill="#A3A3A3" /> <path - d="M504.334 80.9852H513.562C513.562 88.8148 519.434 93.7083 528.941 93.7083C539.427 93.7083 545.579 89.2343 545.579 81.9639C545.579 76.3713 542.783 73.1556 534.814 71.1982L523.209 68.2621C511.465 65.3259 505.732 59.1741 505.732 49.9463C505.732 38.0621 515.659 30.5121 530.2 30.5121C544.461 30.5121 553.689 38.3417 554.108 50.925H544.74C544.461 43.0954 539.008 38.4815 529.92 38.4815C520.413 38.4815 515.1 42.5361 515.1 49.8065C515.1 54.9796 518.735 58.6148 526.145 60.4324L537.75 63.3685C549.494 66.3046 554.807 71.7574 554.807 81.5445C554.807 93.7083 544.461 101.678 529.081 101.678C513.841 101.678 504.334 93.5685 504.334 80.9852Z" + d="M119.807 18.9938V12.749C119.807 10.0586 121.16 8.28144 123.551 8.28144C125.484 8.28144 126.717 9.26876 126.717 12.0579V18.9938H128.384V11.6877C128.384 8.67637 127.055 6.72642 123.841 6.72642C122.15 6.72642 120.604 7.59032 119.831 9.12066L119.589 7.0473H118.139V18.9938H119.807Z" fill="#A3A3A3" /> + <path + d="M130.091 15.6369C130.091 17.8584 131.734 19.29 134.367 19.29C137.025 19.29 138.813 17.8831 138.813 15.7357C138.813 14.0079 137.895 13.0452 135.865 12.5269L133.86 12.0086C132.58 11.6877 131.951 11.0459 131.951 10.1327C131.951 8.84915 132.869 8.13335 134.512 8.13335C136.083 8.13335 137.025 8.94788 137.073 10.3301H138.692C138.62 8.10866 137.025 6.72642 134.561 6.72642C132.048 6.72642 130.333 8.0593 130.333 10.1573C130.333 11.7864 131.323 12.8725 133.353 13.3908L135.358 13.9091C136.735 14.2547 137.218 14.8224 137.218 15.8097C137.218 17.0932 136.155 17.8831 134.343 17.8831C132.7 17.8831 131.686 17.0192 131.686 15.6369H130.091Z" + fill="#A3A3A3" + /> + <path + d="M140.253 13.0206C140.253 16.6489 142.766 19.29 146.196 19.29C149.627 19.29 152.14 16.6489 152.14 13.0206C152.14 9.36749 149.627 6.72642 146.196 6.72642C142.766 6.72642 140.253 9.36749 140.253 13.0206ZM141.944 12.9959C141.944 10.2314 143.684 8.25676 146.196 8.25676C148.685 8.25676 150.449 10.2314 150.449 12.9959C150.449 15.8097 148.685 17.7597 146.196 17.7597C143.684 17.7597 141.944 15.8097 141.944 12.9959Z" + fill="#A3A3A3" + /> + <path d="M156.063 18.9938V1H154.396V18.9938H156.063Z" fill="#A3A3A3" /> + <path + d="M164.03 19.29C166.784 19.29 168.596 17.9324 169.224 15.4148H167.63C167.195 16.9945 165.963 17.8337 164.054 17.8337C161.541 17.8337 160.043 16.1553 159.923 13.2427H169.224V12.4035C169.224 9.02193 167.074 6.72642 163.909 6.72642C160.575 6.72642 158.304 9.29344 158.304 13.0206C158.304 16.7723 160.599 19.29 164.03 19.29ZM163.909 8.18271C166.108 8.18271 167.557 9.71305 167.557 12.0086H159.971C160.261 9.66368 161.71 8.18271 163.909 8.18271Z" + fill="#A3A3A3" + /> + <rect x="172" y="0.977051" width="25.4024" height="11.9004" rx="5.7012" fill="#F3F4F6" /> + <path + d="M178.296 8.92725V4.06507H180.232C181.19 4.06507 181.775 4.56392 181.775 5.3754C181.775 5.91416 181.535 6.29994 181.077 6.49283C181.589 6.65912 181.848 7.0449 181.848 7.61027C181.848 8.44169 181.269 8.92725 180.265 8.92725H178.296ZM180.159 4.81668H179.148V6.14031H180.179C180.644 6.14031 180.904 5.89421 180.904 5.46186C180.904 5.04283 180.638 4.81668 180.159 4.81668ZM180.232 6.85866H179.148V8.17564H180.232C180.711 8.17564 180.977 7.94284 180.977 7.50385C180.977 7.09146 180.704 6.85866 180.232 6.85866ZM183.979 9.01371C183.001 9.01371 182.316 8.30201 182.316 7.28435C182.316 6.25338 182.988 5.54168 183.952 5.54168C184.937 5.54168 185.562 6.20017 185.562 7.22449V7.47059L183.088 7.47724C183.148 8.05591 183.454 8.34857 183.992 8.34857C184.438 8.34857 184.731 8.17564 184.824 7.86302H185.575C185.436 8.58137 184.837 9.01371 183.979 9.01371ZM183.959 6.20682C183.48 6.20682 183.187 6.46623 183.108 6.95843H184.757C184.757 6.50613 184.445 6.20682 183.959 6.20682ZM187.296 8.92725H186.485V6.3199H185.853V5.64145H186.485V4.61714H187.296V5.64145H187.935V6.3199H187.296V8.92725ZM189.398 9.01371C188.699 9.01371 188.274 8.60798 188.274 7.9894C188.274 7.38412 188.713 7.00499 189.491 6.94513L190.475 6.87196V6.7988C190.475 6.35315 190.209 6.17356 189.797 6.17356C189.318 6.17356 189.052 6.37311 189.052 6.71898H188.36C188.36 6.00728 188.945 5.54168 189.837 5.54168C190.721 5.54168 191.267 6.02058 191.267 6.93182V8.92725H190.555L190.495 8.44169C190.355 8.78092 189.91 9.01371 189.398 9.01371ZM189.664 8.40179C190.163 8.40179 190.482 8.10247 190.482 7.59697V7.42403L189.797 7.47724C189.291 7.5238 189.098 7.69008 189.098 7.95614C189.098 8.25545 189.298 8.40179 189.664 8.40179Z" + fill="#1F2937" + /> + <defs> + <linearGradient id="paint0_linear_882_9888" x1="12" y1="17.5" x2="16.0069" y2="13.0061" gradientUnits="userSpaceOnUse"> + <stop stopColor="#FF414C" /> + <stop offset="1" stopColor="#FF414C" stopOpacity="0" /> + </linearGradient> + </defs> + </svg> + ); +}; + +export const AkashConsoleBetaLogoLight = ({ className = "" }: { className?: string }) => { + return ( + <svg xmlns="http://www.w3.org/2000/svg" width="198" height="19" viewBox="0 0 198 19" fill="none" className={className}> + <path d="M13.7518 12.0425L17.1474 18.0147H10.2884L6.85693 12.0425H13.7518Z" fill="url(#paint0_linear_882_9889)" /> + <path d="M17.1455 18.0158L20.5713 12.0436L13.7141 0.0961914H6.85693L17.1455 18.0158Z" fill="#FF414C" /> + <path d="M3.42857 6.06689H10.2858L3.43142 18.0143L0 12.042L3.42857 6.06689Z" fill="#FF414C" /> + <path + d="M35.1479 5.80026L34.9755 7.67249C34.2366 6.21905 32.537 5.43074 30.7143 5.43074C27.2907 5.43074 25 7.91884 25 11.7372C25 15.5309 27.069 18.29 30.6897 18.29C32.6602 18.29 34.1134 17.3293 34.877 16.0729L35.074 17.9698H37.8819V5.80026H35.1479ZM34.8277 11.885C34.8277 14.0775 33.473 15.5802 31.3794 15.5802C29.2858 15.5802 28.005 14.0528 28.005 11.885C28.005 9.71716 29.3104 8.16518 31.404 8.16518C33.4976 8.16518 34.8277 9.69252 34.8277 11.885Z" + fill="#232323" + /> + <path + d="M42.9947 17.9698V14.6933L44.7681 12.8458L47.9455 17.9698H51.3938L46.8617 10.6286L51.4923 5.80026H47.7238L42.9947 10.8996V0H40.0144V17.9698H42.9947Z" + fill="#232323" + /> + <path + d="M61.4851 5.80026L61.3127 7.67249C60.5737 6.21905 58.8742 5.43074 57.0515 5.43074C53.6279 5.43074 51.3372 7.91884 51.3372 11.7372C51.3372 15.5309 53.4062 18.29 57.0269 18.29C58.9974 18.29 60.4506 17.3293 61.2141 16.0729L61.4112 17.9698H64.2191V5.80026H61.4851ZM61.1649 11.885C61.1649 14.0775 59.8102 15.5802 57.7166 15.5802C55.6229 15.5802 54.3421 14.0528 54.3421 11.885C54.3421 9.71716 55.6476 8.16518 57.7412 8.16518C59.8348 8.16518 61.1649 9.69252 61.1649 11.885Z" + fill="#232323" + /> + <path + d="M65.7397 14.2746C65.7397 16.6641 67.6609 18.29 70.6905 18.29C73.6954 18.29 75.7891 16.7627 75.7891 14.2992C75.7891 12.427 74.7546 11.4909 72.7102 11.0228L70.5181 10.5055C69.4836 10.2591 68.9417 9.8157 68.9417 9.12593C68.9417 8.21445 69.6314 7.67249 70.8136 7.67249C71.9713 7.67249 72.661 8.33762 72.6856 9.37228H75.5428C75.5181 7.00735 73.6708 5.43074 70.9368 5.43074C68.1289 5.43074 66.1092 6.85955 66.1092 9.2491C66.1092 11.1952 67.1683 12.2299 69.3604 12.7226L71.5526 13.2399C72.6363 13.4863 72.9565 13.9297 72.9565 14.5455C72.9565 15.4324 72.193 15.999 70.789 15.999C69.4097 15.999 68.6215 15.3338 68.5969 14.2746H65.7397Z" + fill="#232323" + /> + <path + d="M80.4681 17.9698V11.3184C80.4681 9.49545 81.5765 8.16518 83.4731 8.16518C85.0002 8.16518 86.01 9.15057 86.01 11.1952V17.9698H89.015V10.4808C89.015 7.3276 87.4386 5.43074 84.4337 5.43074C82.6849 5.43074 81.2563 6.19441 80.4927 7.37687V0H77.4632V17.9698H80.4681Z" + fill="#232323" + /> + <path + d="M92 12.0452C92 15.797 94.247 18.29 97.557 18.29C100.215 18.29 102.293 16.6856 102.848 14.242H101.157C100.698 15.7723 99.2966 16.7597 97.557 16.7597C95.1892 16.7597 93.6429 14.8591 93.6429 12.0206C93.6429 9.08329 95.31 7.23208 97.6778 7.23208C99.2966 7.23208 100.698 8.14534 101.133 9.7991H102.8C102.341 7.33081 100.36 5.72642 97.6536 5.72642C94.247 5.72642 92 8.29344 92 12.0452Z" + fill="#A3A3A3" + /> + <path + d="M104.069 12.0206C104.069 15.6489 106.582 18.29 110.013 18.29C113.444 18.29 115.956 15.6489 115.956 12.0206C115.956 8.36749 113.444 5.72642 110.013 5.72642C106.582 5.72642 104.069 8.36749 104.069 12.0206ZM105.76 11.9959C105.76 9.23139 107.5 7.25676 110.013 7.25676C112.501 7.25676 114.265 9.23139 114.265 11.9959C114.265 14.8097 112.501 16.7597 110.013 16.7597C107.5 16.7597 105.76 14.8097 105.76 11.9959Z" + fill="#A3A3A3" + /> + <path + d="M119.807 17.9938V11.749C119.807 9.05861 121.16 7.28144 123.551 7.28144C125.484 7.28144 126.717 8.26876 126.717 11.0579V17.9938H128.384V10.6877C128.384 7.67637 127.055 5.72642 123.841 5.72642C122.15 5.72642 120.604 6.59032 119.831 8.12066L119.589 6.0473H118.139V17.9938H119.807Z" + fill="#A3A3A3" + /> + <path + d="M130.091 14.6369C130.091 16.8584 131.734 18.29 134.367 18.29C137.025 18.29 138.813 16.8831 138.813 14.7357C138.813 13.0079 137.895 12.0452 135.865 11.5269L133.86 11.0086C132.58 10.6877 131.951 10.0459 131.951 9.13266C131.951 7.84915 132.869 7.13335 134.512 7.13335C136.083 7.13335 137.025 7.94788 137.073 9.33012H138.692C138.62 7.10866 137.025 5.72642 134.561 5.72642C132.048 5.72642 130.333 7.0593 130.333 9.15734C130.333 10.7864 131.323 11.8725 133.353 12.3908L135.358 12.9091C136.735 13.2547 137.218 13.8224 137.218 14.8097C137.218 16.0932 136.155 16.8831 134.343 16.8831C132.7 16.8831 131.686 16.0192 131.686 14.6369H130.091Z" + fill="#A3A3A3" + /> + <path + d="M140.253 12.0206C140.253 15.6489 142.766 18.29 146.196 18.29C149.627 18.29 152.14 15.6489 152.14 12.0206C152.14 8.36749 149.627 5.72642 146.196 5.72642C142.766 5.72642 140.253 8.36749 140.253 12.0206ZM141.944 11.9959C141.944 9.23139 143.684 7.25676 146.196 7.25676C148.685 7.25676 150.449 9.23139 150.449 11.9959C150.449 14.8097 148.685 16.7597 146.196 16.7597C143.684 16.7597 141.944 14.8097 141.944 11.9959Z" + fill="#A3A3A3" + /> + <path d="M156.063 17.9938V0H154.396V17.9938H156.063Z" fill="#A3A3A3" /> + <path + d="M164.03 18.29C166.784 18.29 168.596 16.9324 169.224 14.4148H167.63C167.195 15.9945 165.963 16.8337 164.054 16.8337C161.541 16.8337 160.043 15.1553 159.923 12.2427H169.224V11.4035C169.224 8.02193 167.074 5.72642 163.909 5.72642C160.575 5.72642 158.304 8.29344 158.304 12.0206C158.304 15.7723 160.599 18.29 164.03 18.29ZM163.909 7.18271C166.108 7.18271 167.557 8.71305 167.557 11.0086H159.971C160.261 8.66368 161.71 7.18271 163.909 7.18271Z" + fill="#A3A3A3" + /> + <rect x="172" width="25.4024" height="11.9004" rx="5.7012" fill="#F3F4F6" /> + <path + d="M178.296 7.9502V3.08802H180.232C181.19 3.08802 181.775 3.58687 181.775 4.39834C181.775 4.93711 181.535 5.32289 181.077 5.51578C181.589 5.68207 181.848 6.06785 181.848 6.63322C181.848 7.46464 181.269 7.9502 180.265 7.9502H178.296ZM180.159 3.83963H179.148V5.16326H180.179C180.644 5.16326 180.904 4.91715 180.904 4.48481C180.904 4.06577 180.638 3.83963 180.159 3.83963ZM180.232 5.88161H179.148V7.19859H180.232C180.711 7.19859 180.977 6.96579 180.977 6.52679C180.977 6.11441 180.704 5.88161 180.232 5.88161ZM183.979 8.03666C183.001 8.03666 182.316 7.32496 182.316 6.3073C182.316 5.27633 182.988 4.56463 183.952 4.56463C184.937 4.56463 185.562 5.22312 185.562 6.24744V6.49354L183.088 6.50019C183.148 7.07886 183.454 7.37152 183.992 7.37152C184.438 7.37152 184.731 7.19859 184.824 6.88597H185.575C185.436 7.60432 184.837 8.03666 183.979 8.03666ZM183.959 5.22977C183.48 5.22977 183.187 5.48918 183.108 5.98138H184.757C184.757 5.52908 184.445 5.22977 183.959 5.22977ZM187.296 7.9502H186.485V5.34284H185.853V4.6644H186.485V3.64008H187.296V4.6644H187.935V5.34284H187.296V7.9502ZM189.398 8.03666C188.699 8.03666 188.274 7.63093 188.274 7.01235C188.274 6.40707 188.713 6.02794 189.491 5.96808L190.475 5.89491V5.82175C190.475 5.3761 190.209 5.19651 189.797 5.19651C189.318 5.19651 189.052 5.39606 189.052 5.74193H188.36C188.36 5.03023 188.945 4.56463 189.837 4.56463C190.721 4.56463 191.267 5.04353 191.267 5.95477V7.9502H190.555L190.495 7.46464C190.355 7.80386 189.91 8.03666 189.398 8.03666ZM189.664 7.42473C190.163 7.42473 190.482 7.12542 190.482 6.61991V6.44698L189.797 6.50019C189.291 6.54675 189.098 6.71303 189.098 6.97909C189.098 7.2784 189.298 7.42473 189.664 7.42473Z" + fill="#1F2937" + /> <defs> - <linearGradient id="paint0_linear_2769_41" x1="66.7855" y1="91.9308" x2="89.0854" y2="66.9204" gradientUnits="userSpaceOnUse"> + <linearGradient id="paint0_linear_882_9889" x1="12" y1="16.5" x2="16.0069" y2="12.0061" gradientUnits="userSpaceOnUse"> <stop stopColor="#FF414C" /> <stop offset="1" stopColor="#FF414C" stopOpacity="0" /> </linearGradient> diff --git a/deploy-web/src/components/layout/AccountMenu.tsx b/deploy-web/src/components/layout/AccountMenu.tsx index 69f3548ce..5de3b18e7 100644 --- a/deploy-web/src/components/layout/AccountMenu.tsx +++ b/deploy-web/src/components/layout/AccountMenu.tsx @@ -11,7 +11,7 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuSeparator } from "../ui/ import { DropdownMenuTrigger } from "@radix-ui/react-dropdown-menu"; import { Settings, MultiplePages, Star, Bell, Book, LogOut } from "iconoir-react"; import { CustomDropdownLinkItem } from "../shared/CustomDropdownLinkItem"; -import ClickAwayListener from "react-click-away-listener"; +import ClickAwayListener from "@mui/material/ClickAwayListener"; export function AccountMenu({}: React.PropsWithChildren<{}>) { const [open, setOpen] = useState(false); @@ -41,8 +41,17 @@ export function AccountMenu({}: React.PropsWithChildren<{}>) { </Avatar> </Button> </DropdownMenuTrigger> - <DropdownMenuContent align="end" onMouseLeave={() => setOpen(false)}> - <ClickAwayListener onClickAway={() => setOpen(false)}> + <DropdownMenuContent + align="end" + onMouseLeave={() => { + setOpen(false); + }} + > + <ClickAwayListener + onClickAway={() => { + setOpen(false); + }} + > <div> {!isLoading && user ? ( <div> @@ -80,7 +89,7 @@ export function AccountMenu({}: React.PropsWithChildren<{}>) { ) : ( <div> <CustomDropdownLinkItem - className="hover:bg-primary-dark bg-primary !text-white hover:text-white" + className="bg-primary !text-white hover:bg-primary/80 hover:text-white" onClick={() => router.push(UrlService.signup())} > Sign up diff --git a/deploy-web/src/components/layout/AppLayoutContainer.tsx b/deploy-web/src/components/layout/AppLayoutContainer.tsx index 98ed0f85d..8f8cd1999 100644 --- a/deploy-web/src/components/layout/AppLayoutContainer.tsx +++ b/deploy-web/src/components/layout/AppLayoutContainer.tsx @@ -7,6 +7,7 @@ import { useSettings } from "@src/context/SettingsProvider"; import { breakpoints } from "@src/utils/responsiveUtils"; import { accountBarHeight, closedDrawerWidth, drawerWidth } from "@src/utils/constants"; import { Sidebar } from "./Sidebar"; +import { cn } from "@src/utils/styleUtils"; export function AppLayoutContainer({ children, version }: React.PropsWithChildren<{ version: string }>) { const [isShowingWelcome, setIsShowingWelcome] = useState(false); @@ -101,8 +102,11 @@ export function AppLayoutContainer({ children, version }: React.PropsWithChildre /> <div - className="ease ml-0 h-full flex-grow transition-[margin-left] duration-300 md:ml-[240px]" - style={{ marginLeft: !smallScreen ? 0 : isNavOpen ? `${drawerWidth}px` : `${closedDrawerWidth}px` }} + className={cn("ease ml-0 h-full flex-grow transition-[margin-left] duration-300", { + ["sm:ml-[240px]"]: isNavOpen, + ["sm:ml-[60px]"]: !isNavOpen + })} + // style={{ marginLeft: !smallScreen ? 0 : isNavOpen ? `${drawerWidth}px` : `${closedDrawerWidth}px` }} // className={classes.viewContentContainer} // sx={{ marginLeft: { xs: 0, sm: 0, md: isNavOpen ? `${drawerWidth}px` : `${closedDrawerWidth}px` }, minWidth: 0 }} // viewContentContainer: { diff --git a/deploy-web/src/components/layout/CustomGoogleAnalytics.tsx b/deploy-web/src/components/layout/CustomGoogleAnalytics.tsx index c60384cc1..2cbb83632 100644 --- a/deploy-web/src/components/layout/CustomGoogleAnalytics.tsx +++ b/deploy-web/src/components/layout/CustomGoogleAnalytics.tsx @@ -1,5 +1,4 @@ "use client"; - import { isProd } from "@src/utils/constants"; import { event, GoogleAnalytics as GAnalytics } from "nextjs-google-analytics"; import { useReportWebVitals } from "next/web-vitals"; diff --git a/deploy-web/src/components/layout/CustomProviders.tsx b/deploy-web/src/components/layout/CustomProviders.tsx index 2d2d9fda4..91aa4f78e 100644 --- a/deploy-web/src/components/layout/CustomProviders.tsx +++ b/deploy-web/src/components/layout/CustomProviders.tsx @@ -18,6 +18,8 @@ import { LocalNoteProvider } from "@src/context/LocalNoteProvider"; import { TemplatesProvider } from "@src/context/TemplatesProvider"; import { StyledEngineProvider, createTheme, ThemeProvider as MuiThemeProvider } from "@mui/material/styles"; import { ChainParamProvider } from "@src/context/ChainParamProvider"; +import { AddressBookProvider } from "@src/context/AddressBookProvider"; +import { BackgroundTaskProvider } from "@src/context/BackgroundTaskProvider"; const theme = createTheme({ palette: { @@ -36,17 +38,7 @@ const theme = createTheme({ } }); -function Providers({ children, version }: React.PropsWithChildren<{ version: string }>) { - //* <PricingProvider> - //* <UserProvider> - // <AddressBookProvider> - //* <SettingsProvider> - //* <CustomChainProvider> - //* <WalletProvider> - //* <CertificateProvider> - //* <TemplatesProvider> - //* <LocalNoteProvider> - // <BackgroundTaskProvider></BackgroundTaskProvider> +function Providers({ children }: React.PropsWithChildren<{}>) { return ( <CustomIntlProvider> <QueryClientProvider client={queryClient}> @@ -56,27 +48,29 @@ function Providers({ children, version }: React.PropsWithChildren<{ version: str <ThemeProvider attribute="class" defaultTheme="light" storageKey="theme" enableSystem disableTransitionOnChange> <PricingProvider> <UserProvider> - {/* <AddressBookProvider> */} - <TooltipProvider> - <SettingsProvider version={version}> - <CustomChainProvider> - <WalletProvider> - <ChainParamProvider> - <CertificateProvider> - <TemplatesProvider> - <LocalNoteProvider> - <ProgressBar height="4px" color={customColors.akashRed} options={{ showSpinner: false }} shallowRouting /> + <AddressBookProvider> + <TooltipProvider> + <SettingsProvider> + <CustomChainProvider> + <WalletProvider> + <ChainParamProvider> + <CertificateProvider> + <BackgroundTaskProvider> + <TemplatesProvider> + <LocalNoteProvider> + <ProgressBar height="4px" color={customColors.akashRed} options={{ showSpinner: false }} shallowRouting /> - {children} - </LocalNoteProvider> - </TemplatesProvider> - </CertificateProvider> - </ChainParamProvider> - </WalletProvider> - </CustomChainProvider> - </SettingsProvider> - </TooltipProvider> - {/* </AddressBookProvider> */} + {children} + </LocalNoteProvider> + </TemplatesProvider> + </BackgroundTaskProvider> + </CertificateProvider> + </ChainParamProvider> + </WalletProvider> + </CustomChainProvider> + </SettingsProvider> + </TooltipProvider> + </AddressBookProvider> </UserProvider> </PricingProvider> </ThemeProvider> diff --git a/deploy-web/src/components/layout/Layout.tsx b/deploy-web/src/components/layout/Layout.tsx index 18d7254a1..0432e6ef7 100644 --- a/deploy-web/src/components/layout/Layout.tsx +++ b/deploy-web/src/components/layout/Layout.tsx @@ -1,44 +1,46 @@ import React, { ReactNode, useEffect, useState } from "react"; -import Box from "@mui/material/Box"; import { IntlProvider } from "react-intl"; import { ErrorBoundary } from "react-error-boundary"; import { ErrorFallback } from "../shared/ErrorFallback"; import { accountBarHeight, closedDrawerWidth, drawerWidth } from "@src/utils/constants"; -import { CircularProgress, Typography, useMediaQuery, useTheme } from "@mui/material"; -import { makeStyles } from "tss-react/mui"; +import { useMediaQuery, useTheme } from "@mui/material"; import { WelcomeModal } from "./WelcomeModal"; import { Sidebar } from "./Sidebar"; import { useSettings } from "@src/context/SettingsProvider"; import { LinearLoadingSkeleton } from "../shared/LinearLoadingSkeleton"; import { Header } from "./Header"; import { useWallet } from "@src/context/WalletProvider"; +import Spinner from "../shared/Spinner"; +import { cn } from "@src/utils/styleUtils"; +import { Nav } from "./Nav"; type Props = { isLoading?: boolean; isUsingSettings?: boolean; isUsingWallet?: boolean; + disableContainer?: boolean; children?: ReactNode; }; -const useStyles = makeStyles()(theme => ({ - root: { - width: "100%" - }, - accountBar: { - height: `${accountBarHeight}px`, - display: "flex", - alignItems: "center", - justifyContent: "space-between", - width: "100%", - borderBottom: `1px solid ${theme.palette.mode === "dark" ? theme.palette.grey[900] : theme.palette.grey[300]}` - }, - viewContentContainer: { - flexGrow: 1, - transition: "margin-left .3s ease" - } -})); - -const Layout: React.FunctionComponent<Props> = ({ children, isLoading, isUsingSettings, isUsingWallet }) => { +// const useStyles = makeStyles()(theme => ({ +// root: { +// width: "100%" +// }, +// accountBar: { +// height: `${accountBarHeight}px`, +// display: "flex", +// alignItems: "center", +// justifyContent: "space-between", +// width: "100%", +// borderBottom: `1px solid ${theme.palette.mode === "dark" ? theme.palette.grey[900] : theme.palette.grey[300]}` +// }, +// viewContentContainer: { +// flexGrow: 1, +// transition: "margin-left .3s ease" +// } +// })); + +const Layout: React.FunctionComponent<Props> = ({ children, isLoading, isUsingSettings, isUsingWallet, disableContainer }) => { const [locale, setLocale] = useState("en-US"); useEffect(() => { @@ -49,16 +51,15 @@ const Layout: React.FunctionComponent<Props> = ({ children, isLoading, isUsingSe return ( <IntlProvider locale={locale} defaultLocale="en-US"> - <LayoutApp isLoading={isLoading} isUsingSettings={isUsingSettings} isUsingWallet={isUsingWallet}> + <LayoutApp isLoading={isLoading} isUsingSettings={isUsingSettings} isUsingWallet={isUsingWallet} disableContainer={disableContainer}> {children} </LayoutApp> </IntlProvider> ); }; -const LayoutApp: React.FunctionComponent<Props> = ({ children, isLoading, isUsingSettings, isUsingWallet }) => { +const LayoutApp: React.FunctionComponent<Props> = ({ children, isLoading, isUsingSettings, isUsingWallet, disableContainer }) => { const theme = useTheme(); - const { classes } = useStyles(); const [isShowingWelcome, setIsShowingWelcome] = useState(false); const [isNavOpen, setIsNavOpen] = useState(true); const [isMobileOpen, setIsMobileOpen] = useState(false); @@ -113,9 +114,9 @@ const LayoutApp: React.FunctionComponent<Props> = ({ children, isLoading, isUsin return ( <> - <WelcomeModal open={isShowingWelcome} onClose={onWelcomeClose} /> + {/* <WelcomeModal open={isShowingWelcome} onClose={onWelcomeClose} /> */} - <Box sx={{ height: "100%" }}> + {/* <Box sx={{ height: "100%" }}> <Box className={classes.root} sx={{ marginTop: `${accountBarHeight}px`, height: "100%" }}> <Box height="100%"> <Header isMobileOpen={isMobileOpen} handleDrawerToggle={handleDrawerToggle} /> @@ -152,21 +153,81 @@ const LayoutApp: React.FunctionComponent<Props> = ({ children, isLoading, isUsin </Box> </Box> </Box> - </Box> + </Box> */} + + <div + className="h-full" + // sx={{ height: "100%" }} + > + <div + className="h-full w-full" + style={{ marginTop: `${accountBarHeight}px` }} + // className={classes.root} sx={{ marginTop: `${accountBarHeight}px`, height: "100%" }} + > + <div + className="h-full" + // height="100%" + > + {/* <Header isMobileOpen={isMobileOpen} handleDrawerToggle={handleDrawerToggle} /> */} + <Nav /> + + <div + className="block h-full w-full flex-grow rounded-none md:flex" + // sx={{ + // display: { xs: "block", sx: "block", md: "flex" }, + // width: "100%", + // borderRadius: 0, + // flexGrow: 1, + // height: "100%" + // }} + > + <Sidebar onOpenMenuClick={onOpenMenuClick} isNavOpen={isNavOpen} handleDrawerToggle={handleDrawerToggle} isMobileOpen={isMobileOpen} /> + + <div + className={cn("ease ml-0 h-full flex-grow transition-[margin-left] duration-300", { + ["sm:ml-[240px]"]: isNavOpen, + ["sm:ml-[57px]"]: !isNavOpen + })} + // style={{ marginLeft: !smallScreen ? 0 : isNavOpen ? `${drawerWidth}px` : `${closedDrawerWidth}px` }} + // className={classes.viewContentContainer} + // sx={{ marginLeft: { xs: 0, sm: 0, md: isNavOpen ? `${drawerWidth}px` : `${closedDrawerWidth}px` }, minWidth: 0 }} + // viewContentContainer: { + // flexGrow: 1, + // transition: "margin-left .3s ease" + // } + > + {isLoading !== undefined && <LinearLoadingSkeleton isLoading={isLoading} />} + + <ErrorBoundary FallbackComponent={ErrorFallback}> + {!isUsingSettings || isSettingsInit ? ( + !isUsingWallet || isWalletLoaded ? ( + <div className={cn({ ["container h-full pb-8 pt-4 sm:pt-8"]: !disableContainer })}>{children}</div> + ) : ( + <Loading text="Loading wallet..." /> + ) + ) : ( + <Loading text="Loading settings..." /> + )} + </ErrorBoundary> + </div> + </div> + </div> + </div> + </div> </> ); }; const Loading: React.FunctionComponent<{ text: string }> = ({ text }) => { return ( - <Box sx={{ display: "flex", alignItems: "center", justifyContent: "center", height: "100%", width: "100%", flexDirection: "column", padding: "3rem 0" }}> - <Box sx={{ paddingBottom: "1rem" }}> - <CircularProgress size="5rem" color="secondary" /> - </Box> + <div className="flex h-full w-full flex-col items-center justify-center pb-12 pt-12"> + <div className="pb-4"> + <Spinner size="large" /> + </div> <div> - <Typography variant="h5">{text}</Typography> + <h5>{text}</h5> </div> - </Box> + </div> ); }; diff --git a/deploy-web/src/components/layout/Nav.tsx b/deploy-web/src/components/layout/Nav.tsx index 66317d77d..72ec003b4 100644 --- a/deploy-web/src/components/layout/Nav.tsx +++ b/deploy-web/src/components/layout/Nav.tsx @@ -1,5 +1,5 @@ "use client"; -import { AkashConsoleDarkLogo, AkashConsoleLightLogo } from "../icons/AkashConsoleLogo"; +import { AkashConsoleBetaLogoDark, AkashConsoleBetaLogoLight } from "../icons/AkashConsoleLogo"; import Link from "next/link"; import { UrlService } from "@src/utils/urlUtils"; import { Button } from "../ui/button"; @@ -16,7 +16,11 @@ export const Nav = () => { <header className="fixed top-0 z-50 w-full border-b border-border bg-popover"> <div className="flex h-14 items-center justify-between pl-4 pr-4"> <Link className="flex items-center space-x-2" href="/"> - {theme === "light" ? <AkashConsoleLightLogo className="h-[25px] max-w-[180px]" /> : <AkashConsoleDarkLogo className="h-[25px] max-w-[180px]" />} + {theme === "light" ? ( + <AkashConsoleBetaLogoLight className="h-[19px] max-w-[200px]" /> + ) : ( + <AkashConsoleBetaLogoDark className="h-[19px] max-w-[200px]" /> + )} </Link> <div style={{ height: `${accountBarHeight}px` }} className={`hidden items-center md:flex `}> diff --git a/deploy-web/src/components/layout/PageHead.tsx b/deploy-web/src/components/layout/PageHead.tsx index d76653550..a4bfec9be 100644 --- a/deploy-web/src/components/layout/PageHead.tsx +++ b/deploy-web/src/components/layout/PageHead.tsx @@ -16,27 +16,27 @@ export const PageHead: React.FunctionComponent<Props> = ({}) => { </Head> <DefaultSeo - titleTemplate="%s | Cloudmos" - defaultTitle="Cloudmos" - description="Cloudmos is the #1 platform to deploy docker containers on the Akash Network, a decentralized super cloud compute marketplace. Explore, deploy and track all in one place!" + titleTemplate="%s | Akash Console" + defaultTitle="Akash Console" + description="Akash Console is the #1 platform to deploy docker containers on the Akash Network, a decentralized super cloud compute marketplace. Explore, deploy and track all in one place!" openGraph={{ type: "website", locale: "en_US", - url: "https://cloudmos.io/", - site_name: "Cloudmos", + url: "https://console.akash.network/", + site_name: "Akash Console", description: "Deploy docker containers on the decentralized supercloud Akash Network.", images: [ { - url: "https://deploy.cloudmos.io/cloudmos-cover.png", - width: 1600, - height: 529, - alt: "Cloudmos Cover Image" + url: "https://console.akash.network/akash-console.png", + width: 1200, + height: 630, + alt: "AkashConsole Cover Image" } ] }} twitter={{ - handle: "@cloudmosio", - site: "@cloudmosio", + handle: "@akashnet_", + site: "@akashnet_", cardType: "summary_large_image" }} /> diff --git a/deploy-web/src/components/layout/Sidebar.tsx b/deploy-web/src/components/layout/Sidebar.tsx index bfa41de54..cbe9c84d0 100644 --- a/deploy-web/src/components/layout/Sidebar.tsx +++ b/deploy-web/src/components/layout/Sidebar.tsx @@ -17,23 +17,27 @@ import { Rocket, Github, X as TwitterX, Discord, Menu, MenuScale } from "iconoir import { Drawer } from "@rewind-ui/core"; import { Home, Cloud, MultiplePages, Tools, Server, OpenInWindow, HelpCircle, Settings } from "iconoir-react"; import { ISidebarGroupMenu } from "@src/types"; +import getConfig from "next/config"; +import { useTheme } from "@mui/material"; + +const { publicRuntimeConfig } = getConfig(); type Props = { children?: ReactNode; - version: string; isMobileOpen: boolean; handleDrawerToggle: () => void; onOpenMenuClick: () => void; isNavOpen: boolean; }; -export const Sidebar: React.FunctionComponent<Props> = ({ isMobileOpen, version, handleDrawerToggle, isNavOpen, onOpenMenuClick }) => { +export const Sidebar: React.FunctionComponent<Props> = ({ isMobileOpen, handleDrawerToggle, isNavOpen, onOpenMenuClick }) => { const [isHovering, setIsHovering] = useState(false); const _isNavOpen = isNavOpen || isHovering; const [, setDeploySdl] = useAtom(sdlStore.deploySdl); + const theme = useTheme(); // TODO Verify - const mdScreen = useMediaQuery(breakpoints.md.mediaQuery); - const mobileScreen = useMediaQuery(breakpoints.xs.mediaQuery); + const smallScreen = useMediaQuery(theme.breakpoints.down("md")); + // const mobileScreen = useMediaQuery(breakpoints.xs.mediaQuery); const routeGroups: ISidebarGroupMenu[] = [ { @@ -106,6 +110,13 @@ export const Sidebar: React.FunctionComponent<Props> = ({ isMobileOpen, version, url: "https://akash.network/about/pricing/custom/", activeRoutes: [], target: "_blank" + }, + { + title: "API", + icon: props => <OpenInWindow {...props} />, + url: "https://api.cloudmos.io/v1/swagger", + activeRoutes: [], + target: "_blank" } ] } @@ -130,7 +141,7 @@ export const Sidebar: React.FunctionComponent<Props> = ({ isMobileOpen, version, const drawer = ( <div style={{ width: _isNavOpen ? drawerWidth : closedDrawerWidth }} - className={`flex h-full flex-col items-center justify-between box-border flex-shrink-0 overflow-y-auto overflow-x-hidden border-r-[1px] border-muted-foreground/20 transition-[width] duration-300 ease-in-out`} + className={`box-border flex h-full flex-shrink-0 flex-col items-center justify-between overflow-y-auto overflow-x-hidden border-r-[1px] border-muted-foreground/20 transition-[width] duration-300 ease-in-out`} > <div className={cn("flex w-full flex-col items-center justify-between", { ["p-2"]: _isNavOpen, ["pb-2 pt-2"]: !_isNavOpen })}> <Link @@ -150,43 +161,42 @@ export const Sidebar: React.FunctionComponent<Props> = ({ isMobileOpen, version, </div> <div className="w-full"> - {!mdScreen && <MobileSidebarUser />} + {smallScreen && <MobileSidebarUser />} {_isNavOpen && ( <div className="pb-4 pl-4 pr-4"> <NodeStatusBar /> <div className="flex items-center justify-center pt-4"> - <Link target="_blank" rel="noreferrer" href="https://twitter.com/akashnet_" className="text-foreground"> - <Button variant="ghost" size="icon"> - <TwitterX width="1.2rem" height="1.2rem" /> - <span className="sr-only">Twitter</span> - </Button> + <Link target="_blank" rel="noreferrer" href="https://twitter.com/akashnet_" className={cn(buttonVariants({ variant: "text", size: "icon" }))}> + <TwitterX width="1.2rem" height="1.2rem" /> + <span className="sr-only">Twitter</span> </Link> - <Link target="_blank" rel="noreferrer" href="https://github.com/akash-network/cloudmos" className="text-foreground"> - <Button variant="ghost" size="icon"> - <Github width="1.2rem" height="1.2rem" /> - <span className="sr-only">GitHub</span> - </Button> + <Link + target="_blank" + rel="noreferrer" + href="https://github.com/akash-network/cloudmos" + className={cn(buttonVariants({ variant: "text", size: "icon" }))} + > + <Github width="1.2rem" height="1.2rem" /> + <span className="sr-only">GitHub</span> </Link> - <Link target="_blank" rel="noreferrer" href="https://discord.akash.network" className="text-foreground"> - <Button variant="ghost" size="icon"> - <Discord width="1.2rem" height="1.2rem" /> - <span className="sr-only">Twitter</span> - </Button> + <Link target="_blank" rel="noreferrer" href="https://discord.akash.network" className={cn(buttonVariants({ variant: "text", size: "icon" }))}> + <Discord width="1.2rem" height="1.2rem" /> + <span className="sr-only">Twitter</span> </Link> {/** TODO */} {/* <ModeToggle /> */} </div> - {version && _isNavOpen && ( + {publicRuntimeConfig?.version && _isNavOpen && ( <div className="flex flex-col items-center justify-center"> - <span className="text-xs font-bold text-muted-foreground"> - <strong>v{version}</strong> - </span> + <div className="text-xs font-bold text-muted-foreground"> + <strong>v{publicRuntimeConfig?.version}</strong> + </div> <Badge color="secondary" className="h-[12px] text-xs font-bold"> beta @@ -196,7 +206,7 @@ export const Sidebar: React.FunctionComponent<Props> = ({ isMobileOpen, version, </div> )} - {mdScreen && ( + {!smallScreen && ( <div className="flex items-center justify-between border-t border-muted-foreground/20 px-3 py-1"> <Button size="icon" variant="ghost" onClick={onToggleMenuClick}> {isNavOpen ? <MenuScale /> : <Menu />} @@ -209,8 +219,14 @@ export const Sidebar: React.FunctionComponent<Props> = ({ isMobileOpen, version, return ( <nav - style={{ width: !mdScreen ? 0 : _isNavOpen || isHovering ? drawerWidth : closedDrawerWidth, height: `calc(100% - ${accountBarHeight}px)` }} - className="ease fixed z-[100] h-full bg-header/95 transition-[width] duration-300 md:flex-shrink-0" + style={{ + // width: !mdScreen ? 0 : _isNavOpen || isHovering ? drawerWidth : closedDrawerWidth, + height: `calc(100% - ${accountBarHeight}px)` + }} + className={cn("ease fixed z-[100] h-full bg-header/95 transition-[width] duration-300 md:flex-shrink-0", { + ["md:w-[240px]"]: _isNavOpen || isHovering, + ["md:w-[57px]"]: !(_isNavOpen || isHovering) + })} > {/* Mobile Drawer */} <Drawer diff --git a/deploy-web/src/app/new-deployment/BidCountdownTimer.tsx b/deploy-web/src/components/new-deployment/BidCountdownTimer.tsx similarity index 95% rename from deploy-web/src/app/new-deployment/BidCountdownTimer.tsx rename to deploy-web/src/components/new-deployment/BidCountdownTimer.tsx index f727c7f9a..254cac4bd 100644 --- a/deploy-web/src/app/new-deployment/BidCountdownTimer.tsx +++ b/deploy-web/src/components/new-deployment/BidCountdownTimer.tsx @@ -2,8 +2,8 @@ import React, { useEffect, useState } from "react"; import { useBlock } from "@src/queries/useBlocksQuery"; import { differenceInSeconds } from "date-fns"; -import { CustomTooltip } from "../../components/shared/CustomTooltip"; -import { Badge } from "../../components/ui/badge"; +import { CustomTooltip } from "../shared/CustomTooltip"; +import { Badge } from "../ui/badge"; import { InfoCircle } from "iconoir-react"; import { cn } from "@src/utils/styleUtils"; diff --git a/deploy-web/src/app/new-deployment/BidGroup.tsx b/deploy-web/src/components/new-deployment/BidGroup.tsx similarity index 95% rename from deploy-web/src/app/new-deployment/BidGroup.tsx rename to deploy-web/src/components/new-deployment/BidGroup.tsx index c6907f209..0fd1f92d0 100644 --- a/deploy-web/src/app/new-deployment/BidGroup.tsx +++ b/deploy-web/src/components/new-deployment/BidGroup.tsx @@ -2,15 +2,15 @@ import { useEffect, useState } from "react"; import { BidRow } from "./BidRow"; import { deploymentGroupResourceSum, getStorageAmount } from "@src/utils/deploymentDetailUtils"; -import { LabelValueOld } from "../../components/shared/LabelValueOld"; -import { SpecDetail } from "../../components/shared/SpecDetail"; +import { LabelValueOld } from "../shared/LabelValueOld"; +import { SpecDetail } from "../shared/SpecDetail"; import { BidDto, DeploymentDto } from "@src/types/deployment"; import { ApiProviderList } from "@src/types/provider"; import { useSettings } from "@src/context/SettingsProvider"; import { mainnetId } from "@src/utils/constants"; -import { FormPaper } from "../../components/sdl/FormPaper"; -import { Alert } from "../../components/ui/alert"; -import { Table, TableBody, TableCell, TableHeader, TableRow } from "../../components/ui/table"; +import { FormPaper } from "../sdl/FormPaper"; +import { Alert } from "../ui/alert"; +import { Table, TableBody, TableCell, TableHeader, TableRow } from "../ui/table"; import { Check } from "iconoir-react"; type Props = { diff --git a/deploy-web/src/app/new-deployment/BidRow.tsx b/deploy-web/src/components/new-deployment/BidRow.tsx similarity index 90% rename from deploy-web/src/app/new-deployment/BidRow.tsx rename to deploy-web/src/components/new-deployment/BidRow.tsx index 13f308731..2cc2e34b8 100644 --- a/deploy-web/src/app/new-deployment/BidRow.tsx +++ b/deploy-web/src/components/new-deployment/BidRow.tsx @@ -1,28 +1,28 @@ "use client"; import { useEffect } from "react"; import { useLocalNotes } from "@src/context/LocalNoteProvider"; -import { PricePerMonth } from "../../components/shared/PricePerMonth"; -import { PriceEstimateTooltip } from "../../components/shared/PriceEstimateTooltip"; -import { FavoriteButton } from "../../components/shared/FavoriteButton"; -import { AuditorButton } from "../../components/providers/AuditorButton"; +import { PricePerMonth } from "../shared/PricePerMonth"; +import { PriceEstimateTooltip } from "../shared/PriceEstimateTooltip"; +import { FavoriteButton } from "../shared/FavoriteButton"; +import { AuditorButton } from "../providers/AuditorButton"; import Link from "next/link"; import { UrlService } from "@src/utils/urlUtils"; import { getSplitText } from "@src/hooks/useShortText"; import { BidDto } from "@src/types/deployment"; import { ApiProviderList } from "@src/types/provider"; import { useProviderStatus } from "@src/queries/useProvidersQuery"; -import { Uptime } from "../../components/providers/Uptime"; +import { Uptime } from "../providers/Uptime"; import { udenomToDenom } from "@src/utils/mathHelpers"; import { hasSomeParentTheClass } from "@src/utils/domUtils"; import { getGpusFromAttributes } from "@src/utils/deploymentUtils"; -import { TableCell, TableRow } from "../../components/ui/table"; +import { TableCell, TableRow } from "../ui/table"; import { cn } from "@src/utils/styleUtils"; import { useTheme } from "next-themes"; -import { CustomTooltip } from "../../components/shared/CustomTooltip"; -import { Badge } from "../../components/ui/badge"; +import { CustomTooltip } from "../shared/CustomTooltip"; +import { Badge } from "../ui/badge"; import { WarningTriangle, CloudXmark } from "iconoir-react"; -import Spinner from "../../components/shared/Spinner"; -import { RadioGroup, RadioGroupItem } from "../../components/ui/radio-group"; +import Spinner from "../shared/Spinner"; +import { RadioGroup, RadioGroupItem } from "../ui/radio-group"; type Props = { bid: BidDto; @@ -151,7 +151,7 @@ export const BidRow: React.FunctionComponent<Props> = ({ bid, selectedBid, handl // sx={{ marginRight: i < gpuModels.length ? ".2rem" : 0 }} variant="default" > - `${gpu.vendor}-${gpu.model}` + {gpu.vendor}-{gpu.model} </Badge> ))} </TableCell> @@ -188,9 +188,9 @@ export const BidRow: React.FunctionComponent<Props> = ({ bid, selectedBid, handl // marginTop: "4px", // fontSize: ".85rem" // }, - <div className="mt-2 flex items-center"> + <div className="mt-2 flex items-center space-x-2"> <CloudXmark - className="mr-2 text-primary" + className="text-xs text-primary" // className={cx(classes.stateIcon, classes.stateInactive)} sx={{ fontSize: "1rem" }} /> <span className="text-sm text-muted-foreground">OFFLINE</span> diff --git a/deploy-web/src/app/new-deployment/CreateLease.tsx b/deploy-web/src/components/new-deployment/CreateLease.tsx similarity index 95% rename from deploy-web/src/app/new-deployment/CreateLease.tsx rename to deploy-web/src/components/new-deployment/CreateLease.tsx index c80a930b9..cdce07fd9 100644 --- a/deploy-web/src/app/new-deployment/CreateLease.tsx +++ b/deploy-web/src/components/new-deployment/CreateLease.tsx @@ -12,26 +12,26 @@ import { TransactionMessageData } from "@src/utils/TransactionMessageData"; import { getDeploymentLocalData } from "@src/utils/deploymentLocalDataUtils"; import { deploymentData } from "@src/utils/deploymentData"; import { UrlService } from "@src/utils/urlUtils"; -import { LinearLoadingSkeleton } from "../../components/shared/LinearLoadingSkeleton"; -import ViewPanel from "../../components/shared/ViewPanel"; +import { LinearLoadingSkeleton } from "../shared/LinearLoadingSkeleton"; +import ViewPanel from "../shared/ViewPanel"; import { event } from "nextjs-google-analytics"; import { AnalyticsEvents } from "@src/utils/analytics"; -import { CustomTooltip } from "../../components/shared/CustomTooltip"; +import { CustomTooltip } from "../shared/CustomTooltip"; import { BidDto } from "@src/types/deployment"; import { BidCountdownTimer } from "./BidCountdownTimer"; -import { CustomNextSeo } from "../../components/shared/CustomNextSeo"; +import { CustomNextSeo } from "../shared/CustomNextSeo"; import { RouteStepKeys } from "@src/utils/constants"; import { useProviderList } from "@src/queries/useProvidersQuery"; -import { useToast } from "../../components/ui/use-toast"; +import { useToast } from "../ui/use-toast"; import { LocalCert } from "@src/context/CertificateProvider/CertificateProviderContext"; -import { Button } from "../../components/ui/button"; -import { Alert } from "../../components/ui/alert"; -import { Checkbox } from "../../components/ui/checkbox"; +import { Button } from "../ui/button"; +import { Alert } from "../ui/alert"; +import { Checkbox } from "../ui/checkbox"; import { ArrowRight, InfoCircle, Xmark, MoreHoriz, Bin, BadgeCheck } from "iconoir-react"; -import Spinner from "../../components/shared/Spinner"; -import { InputWithIcon } from "../../components/ui/input"; -import { CustomDropdownLinkItem } from "../../components/shared/CustomDropdownLinkItem"; -import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from "../../components/ui/dropdown-menu"; +import Spinner from "../shared/Spinner"; +import { InputWithIcon } from "../ui/input"; +import { CustomDropdownLinkItem } from "../shared/CustomDropdownLinkItem"; +import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from "../ui/dropdown-menu"; const yaml = require("js-yaml"); diff --git a/deploy-web/src/app/new-deployment/DeployOptionBox.tsx b/deploy-web/src/components/new-deployment/DeployOptionBox.tsx similarity index 87% rename from deploy-web/src/app/new-deployment/DeployOptionBox.tsx rename to deploy-web/src/components/new-deployment/DeployOptionBox.tsx index 17eeff907..80ab00ab3 100644 --- a/deploy-web/src/app/new-deployment/DeployOptionBox.tsx +++ b/deploy-web/src/components/new-deployment/DeployOptionBox.tsx @@ -1,6 +1,6 @@ "use client"; -import { Avatar, AvatarFallback, AvatarImage } from "../../components/ui/avatar"; -import { Card, CardContent, CardHeader } from "../../components/ui/card"; +import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar"; +import { Card, CardContent, CardHeader } from "../ui/card"; import { MediaImage } from "iconoir-react"; type Props = { diff --git a/deploy-web/src/app/new-deployment/ManifestEdit.tsx b/deploy-web/src/components/new-deployment/ManifestEdit.tsx similarity index 90% rename from deploy-web/src/app/new-deployment/ManifestEdit.tsx rename to deploy-web/src/components/new-deployment/ManifestEdit.tsx index 33053c312..54e9cfea3 100644 --- a/deploy-web/src/app/new-deployment/ManifestEdit.tsx +++ b/deploy-web/src/components/new-deployment/ManifestEdit.tsx @@ -6,15 +6,15 @@ import { useRouter } from "next/navigation"; import { Timer } from "@src/utils/timer"; import { defaultInitialDeposit, RouteStepKeys } from "@src/utils/constants"; import { deploymentData } from "@src/utils/deploymentData"; -import { LinkTo } from "../../components/shared/LinkTo"; -import { DynamicMonacoEditor } from "../../components/shared/DynamicMonacoEditor"; -import ViewPanel from "../../components/shared/ViewPanel"; +import { LinkTo } from "../shared/LinkTo"; +import { DynamicMonacoEditor } from "../shared/DynamicMonacoEditor"; +import ViewPanel from "../shared/ViewPanel"; import { TransactionMessageData } from "@src/utils/TransactionMessageData"; import { saveDeploymentManifestAndName } from "@src/utils/deploymentLocalDataUtils"; import { UrlService, handleDocClick } from "@src/utils/urlUtils"; import { event } from "nextjs-google-analytics"; import { AnalyticsEvents } from "@src/utils/analytics"; -import { PrerequisiteList } from "../../components/shared/PrerequisiteList"; +import { PrerequisiteList } from "../shared/PrerequisiteList"; import { TemplateCreation } from "@src/types"; import { useCertificate } from "@src/context/CertificateProvider"; import { generateCertificate } from "@src/utils/certificateUtils"; @@ -27,15 +27,14 @@ import { useChainParam } from "@src/context/ChainParamProvider"; import { useMediaQuery } from "usehooks-ts"; import { breakpoints } from "@src/utils/responsiveUtils"; import { EncodeObject } from "@cosmjs/proto-signing"; -import { Alert } from "../../components/ui/alert"; -import { Button } from "../../components/ui/button"; +import { Alert } from "../ui/alert"; +import { Button } from "../ui/button"; import { ArrowRight, InfoCircle } from "iconoir-react"; -import Spinner from "../../components/shared/Spinner"; -import { CustomTooltip } from "../../components/shared/CustomTooltip"; -import { InputWithIcon } from "../../components/ui/input"; -import { DeploymentDepositModal } from "../../components/deploymentDetail/DeploymentDepositModal"; - -const yaml = require("js-yaml"); +import Spinner from "../shared/Spinner"; +import { CustomTooltip } from "../shared/CustomTooltip"; +import { InputWithIcon } from "../ui/input"; +import { DeploymentDepositModal } from "../deployments/DeploymentDepositModal"; +import { CustomNextSeo } from "../shared/CustomNextSeo"; type Props = { selectedTemplate: TemplateCreation; @@ -214,11 +213,10 @@ export const ManifestEdit: React.FunctionComponent<Props> = ({ editedManifest, s return ( <> - {/* TODO */} - {/* <CustomNextSeo + <CustomNextSeo title="Create Deployment - Manifest Edit" url={`https://deploy.cloudmos.io${UrlService.newDeployment({ step: RouteStepKeys.editDeployment })}`} - /> */} + /> <div className="mb-2 pt-4"> <div className="mb-2 flex flex-col items-end justify-between md:flex-row"> @@ -263,13 +261,7 @@ export const ManifestEdit: React.FunctionComponent<Props> = ({ editedManifest, s </div> </div> - {/* <ButtonGroup size="small"> */} - <Button - variant={selectedSdlEditMode === "builder" ? "default" : "outline"} - // color={selectedSdlEditMode === "builder" ? "secondary" : "primary"} - onClick={() => onModeChange("builder")} - size="sm" - > + <Button variant={selectedSdlEditMode === "builder" ? "default" : "outline"} onClick={() => onModeChange("builder")} size="sm"> Builder </Button> <Button @@ -280,11 +272,9 @@ export const ManifestEdit: React.FunctionComponent<Props> = ({ editedManifest, s > YAML </Button> - {/* </ButtonGroup> */} </div> - {/** TODO Warning alert variant */} - {parsingError && <Alert variant="destructive">{parsingError}</Alert>} + {parsingError && <Alert variant="warning">{parsingError}</Alert>} {selectedSdlEditMode === "yaml" && ( <ViewPanel stickToBottom style={{ overflow: "hidden", margin: !smallScreen ? "0 -1rem" : 0 }}> diff --git a/deploy-web/src/app/new-deployment/NewDeploymentContainer.tsx b/deploy-web/src/components/new-deployment/NewDeploymentContainer.tsx similarity index 79% rename from deploy-web/src/app/new-deployment/NewDeploymentContainer.tsx rename to deploy-web/src/components/new-deployment/NewDeploymentContainer.tsx index 9582991c1..4b4644d82 100644 --- a/deploy-web/src/app/new-deployment/NewDeploymentContainer.tsx +++ b/deploy-web/src/components/new-deployment/NewDeploymentContainer.tsx @@ -1,11 +1,7 @@ "use client"; import { useEffect, useState } from "react"; import { useRouter, useSearchParams } from "next/navigation"; -import { CustomizedSteppers } from "@src/app/new-deployment/Stepper"; -import { TemplateList } from "@src/app/new-deployment/TemplateList"; import { RouteStepKeys } from "@src/utils/constants"; -import { ManifestEdit } from "@src/app/new-deployment/ManifestEdit"; -import { CreateLease } from "@src/app/new-deployment/CreateLease"; import { useTemplates } from "@src/context/TemplatesProvider"; import { useLocalNotes } from "@src/context/LocalNoteProvider"; import { UrlService } from "@src/utils/urlUtils"; @@ -14,6 +10,11 @@ import { hardcodedTemplates } from "@src/utils/templates"; import sdlStore from "@src/store/sdlStore"; import { useAtomValue } from "jotai"; import { PageContainer } from "@src/components/shared/PageContainer"; +import Layout from "../layout/Layout"; +import { CustomizedSteppers } from "./Stepper"; +import { TemplateList } from "./TemplateList"; +import { ManifestEdit } from "./ManifestEdit"; +import { CreateLease } from "./CreateLease"; export function NewDeploymentContainer() { const { isLoading: isLoadingTemplates, templates } = useTemplates(); @@ -111,14 +112,20 @@ export function NewDeploymentContainer() { } return ( - <PageContainer isLoading={isLoadingTemplates} isUsingSettings isUsingWallet className="h-full"> - <div className="flex w-full items-center">{activeStep !== null && <CustomizedSteppers activeStep={activeStep} />}</div> - - {activeStep === 0 && <TemplateList setSelectedTemplate={setSelectedTemplate} setEditedManifest={setEditedManifest} />} - {activeStep === 1 && ( - <ManifestEdit selectedTemplate={selectedTemplate as TemplateCreation} editedManifest={editedManifest as string} setEditedManifest={setEditedManifest} /> - )} - {activeStep === 2 && <CreateLease dseq={dseq as string} />} - </PageContainer> + <Layout isLoading={isLoadingTemplates} isUsingSettings isUsingWallet> + <PageContainer className="h-full"> + <div className="flex w-full items-center">{activeStep !== null && <CustomizedSteppers activeStep={activeStep} />}</div> + + {activeStep === 0 && <TemplateList setSelectedTemplate={setSelectedTemplate} setEditedManifest={setEditedManifest} />} + {activeStep === 1 && ( + <ManifestEdit + selectedTemplate={selectedTemplate as TemplateCreation} + editedManifest={editedManifest as string} + setEditedManifest={setEditedManifest} + /> + )} + {activeStep === 2 && <CreateLease dseq={dseq as string} />} + </PageContainer> + </Layout> ); } diff --git a/deploy-web/src/app/new-deployment/SdlBuilder.tsx b/deploy-web/src/components/new-deployment/SdlBuilder.tsx similarity index 94% rename from deploy-web/src/app/new-deployment/SdlBuilder.tsx rename to deploy-web/src/components/new-deployment/SdlBuilder.tsx index abb0603ea..d03c224bc 100644 --- a/deploy-web/src/app/new-deployment/SdlBuilder.tsx +++ b/deploy-web/src/components/new-deployment/SdlBuilder.tsx @@ -5,11 +5,11 @@ import { useFieldArray, useForm } from "react-hook-form"; import { SdlBuilderFormValues, Service } from "@src/types"; import { nanoid } from "nanoid"; import { generateSdl } from "@src/utils/sdl/sdlGenerator"; -import { SimpleServiceFormControl } from "../../components/sdl/SimpleServiceFormControl"; +import { SimpleServiceFormControl } from "../sdl/SimpleServiceFormControl"; import { importSimpleSdl } from "@src/utils/sdl/sdlImport"; -import Spinner from "../../components/shared/Spinner"; -import { Button } from "../../components/ui/button"; -import { Alert } from "../../components/ui/alert"; +import Spinner from "../shared/Spinner"; +import { Button } from "../ui/button"; +import { Alert } from "../ui/alert"; import { useGpuModels } from "@src/queries/useGpuQuery"; interface Props { diff --git a/deploy-web/src/app/new-deployment/Stepper.tsx b/deploy-web/src/components/new-deployment/Stepper.tsx similarity index 89% rename from deploy-web/src/app/new-deployment/Stepper.tsx rename to deploy-web/src/components/new-deployment/Stepper.tsx index 46178604e..4a0020eaa 100644 --- a/deploy-web/src/app/new-deployment/Stepper.tsx +++ b/deploy-web/src/components/new-deployment/Stepper.tsx @@ -3,6 +3,7 @@ import { useRouter } from "next/navigation"; import { UrlService } from "@src/utils/urlUtils"; import { RouteStepKeys } from "@src/utils/constants"; import { Check } from "iconoir-react"; +import React from "react"; interface Step { id: number; @@ -17,10 +18,12 @@ const steps: Step[] = [ export const CustomizedSteppers = ({ activeStep }: React.PropsWithChildren<{ activeStep: number }>) => { const router = useRouter(); - // TODO - function onChooseTemplateClick(ev) { + function onChooseTemplateClick(ev: React.MouseEvent<HTMLAnchorElement>, step: Step) { ev.preventDefault(); - router.replace(UrlService.newDeployment({ step: RouteStepKeys.chooseTemplate })); + + if (step.id === 0) { + router.replace(UrlService.newDeployment({ step: RouteStepKeys.chooseTemplate })); + } } return ( @@ -29,7 +32,7 @@ export const CustomizedSteppers = ({ activeStep }: React.PropsWithChildren<{ act {steps.map((step, stepIdx) => ( <li key={step.name} className="relative md:flex md:flex-1"> {step.id < activeStep ? ( - <a href="#" className="group flex w-full items-center"> + <a href="#" className="group flex w-full items-center" onClick={ev => onChooseTemplateClick(ev, step)}> <span className="flex items-center px-6 py-4 text-sm font-medium"> <span className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full bg-primary group-hover:bg-primary/80"> <Check className="h-6 w-6 text-white" aria-hidden="true" /> @@ -40,7 +43,7 @@ export const CustomizedSteppers = ({ activeStep }: React.PropsWithChildren<{ act ) : step.id === activeStep ? ( <div className="flex items-center px-6 py-4 text-sm font-medium" aria-current="step"> <span className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full border-2 border-primary"> - <span className="text-primary font-bold">{step.id + 1}</span> + <span className="font-bold text-primary">{step.id + 1}</span> </span> <span className="ml-4 text-sm font-bold text-primary">{step.name}</span> </div> diff --git a/deploy-web/src/app/new-deployment/TemplateList.tsx b/deploy-web/src/components/new-deployment/TemplateList.tsx similarity index 96% rename from deploy-web/src/app/new-deployment/TemplateList.tsx rename to deploy-web/src/components/new-deployment/TemplateList.tsx index fd82fedb8..fbb4b35a3 100644 --- a/deploy-web/src/app/new-deployment/TemplateList.tsx +++ b/deploy-web/src/components/new-deployment/TemplateList.tsx @@ -9,11 +9,11 @@ import { DeployOptionBox } from "./DeployOptionBox"; import Link from "next/link"; import { ApiTemplate, TemplateCreation } from "@src/types"; import { helloWorldTemplate, ubuntuTemplate } from "@src/utils/templates"; -import { CustomNextSeo } from "../../components/shared/CustomNextSeo"; +import { CustomNextSeo } from "../shared/CustomNextSeo"; import { useAtom } from "jotai"; import sdlStore from "@src/store/sdlStore"; import { Rocket, Cpu, Wrench, Page, ArrowRight } from "iconoir-react"; -import { Button, buttonVariants } from "../../components/ui/button"; +import { Button, buttonVariants } from "../ui/button"; import { cn } from "@src/utils/styleUtils"; import { NavArrowLeft } from "iconoir-react"; import { usePreviousRoute } from "@src/hooks/usePreviousRoute"; @@ -89,11 +89,10 @@ export const TemplateList: React.FunctionComponent<Props> = ({ setSelectedTempla return ( <> - {/* TODO */} - {/* <CustomNextSeo + <CustomNextSeo title="Create Deployment - Template List" url={`https://deploy.cloudmos.io${UrlService.newDeployment({ step: RouteStepKeys.chooseTemplate })}`} - /> */} + /> <div className="mb-8 mt-8 flex items-center"> <Button aria-label="back" onClick={handleBackClick} size="icon" variant="ghost"> diff --git a/deploy-web/src/app/providers/[owner]/ActiveLeasesGraph.tsx b/deploy-web/src/components/providers/ActiveLeasesGraph.tsx similarity index 97% rename from deploy-web/src/app/providers/[owner]/ActiveLeasesGraph.tsx rename to deploy-web/src/components/providers/ActiveLeasesGraph.tsx index 99f9cb52b..0f7dabbce 100644 --- a/deploy-web/src/app/providers/[owner]/ActiveLeasesGraph.tsx +++ b/deploy-web/src/components/providers/ActiveLeasesGraph.tsx @@ -13,7 +13,7 @@ import Spinner from "@src/components/shared/Spinner"; import { TimeRange } from "@src/components/shared/TimeRange"; import { selectedRangeValues } from "@src/utils/constants"; -const Graph = dynamic(() => import("../../../components/graph/Graph"), { +const Graph = dynamic(() => import("../graph/Graph"), { ssr: false }); diff --git a/deploy-web/src/app/providers/CapacityIcon.tsx b/deploy-web/src/components/providers/CapacityIcon.tsx similarity index 100% rename from deploy-web/src/app/providers/CapacityIcon.tsx rename to deploy-web/src/components/providers/CapacityIcon.tsx diff --git a/deploy-web/src/app/providers/[owner]/edit/EditProviderContainer.tsx b/deploy-web/src/components/providers/EditProviderContainer.tsx similarity index 92% rename from deploy-web/src/app/providers/[owner]/edit/EditProviderContainer.tsx rename to deploy-web/src/components/providers/EditProviderContainer.tsx index 25ca0a443..ba3e9b7cf 100644 --- a/deploy-web/src/app/providers/[owner]/edit/EditProviderContainer.tsx +++ b/deploy-web/src/components/providers/EditProviderContainer.tsx @@ -4,12 +4,13 @@ import { useProviderAttributesSchema, useProviderDetail } from "@src/queries/use import { getProviderNameFromUri } from "@src/utils/providerUtils"; import Link from "next/link"; import { UrlService } from "@src/utils/urlUtils"; -import { PageContainer } from "@src/components/shared/PageContainer"; import { EditProviderForm } from "./EditProviderForm"; import { buttonVariants } from "@src/components/ui/button"; import { NavArrowLeft } from "iconoir-react"; import { cn } from "@src/utils/styleUtils"; import Spinner from "@src/components/shared/Spinner"; +import { NextSeo } from "next-seo"; +import Layout from "../layout/Layout"; type Props = { owner: string; @@ -29,7 +30,9 @@ export const EditProviderContainer: React.FunctionComponent<Props> = ({ owner }) }; return ( - <PageContainer isLoading={isLoadingSchema || isLoadingProvider}> + <Layout isLoading={isLoadingSchema || isLoadingProvider}> + <NextSeo title={`Edit Provider ${owner}`} /> + {provider && providerAttributesSchema && ( <> <div className="flex items-center"> @@ -63,6 +66,6 @@ export const EditProviderContainer: React.FunctionComponent<Props> = ({ owner }) <Spinner size="large" /> </div> )} - </PageContainer> + </Layout> ); }; diff --git a/deploy-web/src/app/providers/[owner]/edit/EditProviderForm.tsx b/deploy-web/src/components/providers/EditProviderForm.tsx similarity index 100% rename from deploy-web/src/app/providers/[owner]/edit/EditProviderForm.tsx rename to deploy-web/src/components/providers/EditProviderForm.tsx diff --git a/deploy-web/src/app/providers/[owner]/leases/LeaseList.tsx b/deploy-web/src/components/providers/LeaseList.tsx similarity index 100% rename from deploy-web/src/app/providers/[owner]/leases/LeaseList.tsx rename to deploy-web/src/components/providers/LeaseList.tsx diff --git a/deploy-web/src/app/providers/[owner]/leases/LeaseListContainer.tsx b/deploy-web/src/components/providers/LeaseListContainer.tsx similarity index 76% rename from deploy-web/src/app/providers/[owner]/leases/LeaseListContainer.tsx rename to deploy-web/src/components/providers/LeaseListContainer.tsx index c3e54358b..466ad113f 100644 --- a/deploy-web/src/app/providers/[owner]/leases/LeaseListContainer.tsx +++ b/deploy-web/src/components/providers/LeaseListContainer.tsx @@ -1,14 +1,15 @@ "use client"; import { useState, useEffect } from "react"; -import { useRouter } from "next/navigation"; import { useAllLeases } from "@src/queries/useLeaseQuery"; import { useWallet } from "@src/context/WalletProvider"; import { ClientProviderDetailWithStatus } from "@src/types/provider"; import { useProviderDetail, useProviderStatus } from "@src/queries/useProvidersQuery"; import { LeaseList } from "./LeaseList"; -import { PageContainer } from "@src/components/shared/PageContainer"; import { LeaseDto } from "@src/types/deployment"; -import ProviderDetailLayout, { ProviderDetailTabs } from "../ProviderDetailLayout"; +import ProviderDetailLayout, { ProviderDetailTabs } from "./ProviderDetailLayout"; +import { CustomNextSeo } from "../shared/CustomNextSeo"; +import { UrlService } from "@src/utils/urlUtils"; +import Layout from "../layout/Layout"; type Props = { owner: string; @@ -66,19 +67,12 @@ export const LeaseListContainer: React.FunctionComponent<Props> = ({ owner }) => }, [leases, provider]); return ( - <PageContainer isLoading={isLoadingLeases || isLoadingProvider || isLoadingStatus}> + <Layout isLoading={isLoadingLeases || isLoadingProvider || isLoadingStatus}> + <CustomNextSeo title={`Provider leases for ${owner}`} url={`https://deploy.cloudmos.io${UrlService.providerDetailLeases(owner)}`} /> + <ProviderDetailLayout address={owner} page={ProviderDetailTabs.LEASES} refresh={refresh} provider={provider as ClientProviderDetailWithStatus}> <LeaseList isLoadingLeases={isLoadingLeases} leases={filteredLeases} /> </ProviderDetailLayout> - </PageContainer> + </Layout> ); - // return ( - // <Layout isLoading={isLoadingLeases || isLoadingProvider || isLoadingStatus}> - // <CustomNextSeo title={`Provider leases for ${owner}`} url={`https://deploy.cloudmos.io${UrlService.providerDetailLeases(owner)}`} /> - - // <ProviderDetailLayout address={owner} page={ProviderDetailTabs.LEASES} refresh={refresh} provider={provider}> - // <LeaseList isLoadingLeases={isLoadingLeases} leases={filteredLeases} /> - // </ProviderDetailLayout> - // </Layout> - // ); }; diff --git a/deploy-web/src/app/providers/[owner]/leases/LeaseRow.tsx b/deploy-web/src/components/providers/LeaseRow.tsx similarity index 100% rename from deploy-web/src/app/providers/[owner]/leases/LeaseRow.tsx rename to deploy-web/src/components/providers/LeaseRow.tsx diff --git a/deploy-web/src/app/providers/[owner]/ProviderDetail.tsx b/deploy-web/src/components/providers/ProviderDetail.tsx similarity index 93% rename from deploy-web/src/app/providers/[owner]/ProviderDetail.tsx rename to deploy-web/src/components/providers/ProviderDetail.tsx index 128ec5d41..fa5e833c7 100644 --- a/deploy-web/src/app/providers/[owner]/ProviderDetail.tsx +++ b/deploy-web/src/components/providers/ProviderDetail.tsx @@ -5,10 +5,9 @@ import { useWallet } from "@src/context/WalletProvider"; import { ApiProviderDetail, ClientProviderDetailWithStatus } from "@src/types/provider"; import { useProviderAttributesSchema, useProviderDetail, useProviderStatus } from "@src/queries/useProvidersQuery"; import { LabelValue } from "@src/components/shared/LabelValue"; -import { CustomNoDivTooltip, CustomTooltip } from "@src/components/shared/CustomTooltip"; +import { CustomNoDivTooltip } from "@src/components/shared/CustomTooltip"; import { FormattedDate } from "react-intl"; import dynamic from "next/dynamic"; -import { PageContainer } from "@src/components/shared/PageContainer"; import ProviderDetailLayout, { ProviderDetailTabs } from "./ProviderDetailLayout"; import { Alert } from "@src/components/ui/alert"; import { ActiveLeasesGraph } from "./ActiveLeasesGraph"; @@ -17,8 +16,11 @@ import { cn } from "@src/utils/styleUtils"; import { Card, CardContent } from "@src/components/ui/card"; import { ProviderSpecs } from "./ProviderSpecs"; import { Check } from "iconoir-react"; +import { CustomNextSeo } from "../shared/CustomNextSeo"; +import Layout from "../layout/Layout"; +import { UrlService } from "@src/utils/urlUtils"; -const NetworkCapacity = dynamic(() => import("../../../components/providers/NetworkCapacity"), { +const NetworkCapacity = dynamic(() => import("./NetworkCapacity"), { ssr: false }); @@ -27,17 +29,6 @@ type Props = { _provider: ApiProviderDetail; }; -// const useStyles = makeStyles()(theme => ({ -// grid: { -// display: "grid", -// gridTemplateColumns: "repeat(2,1fr)", -// gap: "1rem", -// [theme.breakpoints.down("sm")]: { -// gridTemplateColumns: "repeat(1,1fr)" -// } -// } -// })); - export const ProviderDetail: React.FunctionComponent<Props> = ({ owner, _provider }) => { const [provider, setProvider] = useState<ClientProviderDetailWithStatus>(_provider as ClientProviderDetailWithStatus); const { address } = useWallet(); @@ -85,7 +76,9 @@ export const ProviderDetail: React.FunctionComponent<Props> = ({ owner, _provide }; return ( - <PageContainer isLoading={isLoading}> + <Layout isLoading={isLoading}> + <CustomNextSeo title={`Provider detail ${provider?.name || provider?.owner}`} url={`https://deploy.cloudmos.io${UrlService.providerDetail(owner)}`} /> + <ProviderDetailLayout address={owner} page={ProviderDetailTabs.DETAIL} refresh={refresh} provider={provider}> {!provider && isLoading && ( <div className="flex items-center justify-center"> @@ -207,6 +200,6 @@ export const ProviderDetail: React.FunctionComponent<Props> = ({ owner, _provide </> )} </ProviderDetailLayout> - </PageContainer> + </Layout> ); }; diff --git a/deploy-web/src/app/providers/[owner]/ProviderDetailLayout.tsx b/deploy-web/src/components/providers/ProviderDetailLayout.tsx similarity index 99% rename from deploy-web/src/app/providers/[owner]/ProviderDetailLayout.tsx rename to deploy-web/src/components/providers/ProviderDetailLayout.tsx index 0f3034823..7b1d1e108 100644 --- a/deploy-web/src/app/providers/[owner]/ProviderDetailLayout.tsx +++ b/deploy-web/src/components/providers/ProviderDetailLayout.tsx @@ -73,7 +73,7 @@ const ProviderDetailLayout: React.FunctionComponent<Props> = ({ children, page, } return ( - <div> + <div className="pb-12"> <div className="mb-2 flex items-center"> <Button aria-label="back" onClick={handleBackClick} size="sm"> <NavArrowLeft /> diff --git a/deploy-web/src/app/providers/ProviderList.tsx b/deploy-web/src/components/providers/ProviderList.tsx similarity index 92% rename from deploy-web/src/app/providers/ProviderList.tsx rename to deploy-web/src/components/providers/ProviderList.tsx index bf0e1b5a0..283822c32 100644 --- a/deploy-web/src/app/providers/ProviderList.tsx +++ b/deploy-web/src/components/providers/ProviderList.tsx @@ -5,14 +5,11 @@ import { useLocalNotes } from "@src/context/LocalNoteProvider"; import { useAllLeases } from "@src/queries/useLeaseQuery"; import { useWallet } from "@src/context/WalletProvider"; import { useNetworkCapacity, useProviderList } from "@src/queries/useProvidersQuery"; -import { ProviderMap } from "@src/app/providers/ProviderMap"; -import { ProviderTable } from "@src/app/providers/ProviderTable"; import dynamic from "next/dynamic"; import { UrlService } from "@src/utils/urlUtils"; import { useSelectedNetwork } from "@src/hooks/useSelectedNetwork"; import { ClientProviderList } from "@src/types/provider"; import { useRouter, useSearchParams } from "next/navigation"; -import { PageContainer } from "@src/components/shared/PageContainer"; import Spinner from "@src/components/shared/Spinner"; import { Button } from "@src/components/ui/button"; import { OpenNewWindow, Refresh, Xmark } from "iconoir-react"; @@ -22,8 +19,12 @@ import { InputWithIcon } from "@src/components/ui/input"; import { FormItem } from "@src/components/ui/form"; import { Label } from "@src/components/ui/label"; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@src/components/ui/select"; +import Layout from "../layout/Layout"; +import { ProviderMap } from "./ProviderMap"; +import { ProviderTable } from "./ProviderTable"; +import { CustomNextSeo } from "../shared/CustomNextSeo"; -const NetworkCapacity = dynamic(() => import("../../components/providers/NetworkCapacity"), { +const NetworkCapacity = dynamic(() => import("./NetworkCapacity"), { ssr: false }); @@ -41,7 +42,7 @@ const sortOptions: { id: SortId; title: string }[] = [ export const ProviderList: React.FunctionComponent<Props> = ({}) => { const { address } = useWallet(); - const [page, setPage] = useState(1); + const [pageIndex, setPageIndex] = useState(0); const [isFilteringActive, setIsFilteringActive] = useState(true); const [isFilteringFavorites, setIsFilteringFavorites] = useState(false); const [isFilteringAudited, setIsFilteringAudited] = useState(true); @@ -55,7 +56,7 @@ export const ProviderList: React.FunctionComponent<Props> = ({}) => { const { data: providers, isFetching: isLoadingProviders, refetch: getProviders } = useProviderList(); const { data: leases, isFetching: isLoadingLeases, refetch: getLeases } = useAllLeases(address, { enabled: false }); const { data: networkCapacity, isFetching: isLoadingNetworkCapacity } = useNetworkCapacity(); - const start = (page - 1) * pageSize; + const start = pageIndex * pageSize; const end = start + pageSize; const currentPageProviders = filteredProviders.slice(start, end); const pageCount = Math.ceil(filteredProviders.length / pageSize); @@ -134,28 +135,28 @@ export const ProviderList: React.FunctionComponent<Props> = ({}) => { }; const handleChangePage = (newPage: number) => { - setPage(newPage); + setPageIndex(newPage); }; const onIsFilteringActiveClick = (value: boolean) => { - setPage(1); + setPageIndex(0); setIsFilteringActive(value); }; const onIsFilteringFavoritesClick = (value: boolean) => { - setPage(1); + setPageIndex(0); setIsFilteringFavorites(value); }; const onIsFilteringAuditedClick = (value: boolean) => { - setPage(1); + setPageIndex(0); setIsFilteringAudited(value); }; const onSearchChange = event => { const value = event.target.value; setSearch(value); - setPage(1); + setPage(0); }; const handleSortChange = event => { @@ -171,7 +172,13 @@ export const ProviderList: React.FunctionComponent<Props> = ({}) => { }; return ( - <PageContainer isLoading={isLoadingProviders || isLoadingLeases || isLoadingNetworkCapacity}> + <Layout isLoading={isLoadingProviders || isLoadingLeases || isLoadingNetworkCapacity}> + <CustomNextSeo + title="Providers" + url={`https://deploy.cloudmos.io${UrlService.providers()}`} + description="Explore all the providers available on the Akash Network." + /> + <h1 className="mb-8 text-2xl font-bold">Network Capacity</h1> {providers && providers.length > 0 && ( @@ -330,12 +337,18 @@ export const ProviderList: React.FunctionComponent<Props> = ({}) => { {(providers?.length || 0) > 0 && ( <div className="px-4 pb-8 pt-4"> - <CustomPagination pageSize={pageSize} setPageIndex={handleChangePage} pageIndex={page} totalPageCount={pageCount} setPageSize={setPageSize} /> + <CustomPagination + pageSize={pageSize} + setPageIndex={handleChangePage} + pageIndex={pageIndex} + totalPageCount={pageCount} + setPageSize={setPageSize} + /> </div> )} </div> </> )} - </PageContainer> + </Layout> ); }; diff --git a/deploy-web/src/app/providers/ProviderMap.tsx b/deploy-web/src/components/providers/ProviderMap.tsx similarity index 98% rename from deploy-web/src/app/providers/ProviderMap.tsx rename to deploy-web/src/components/providers/ProviderMap.tsx index f044202c8..53e8fd7c6 100644 --- a/deploy-web/src/app/providers/ProviderMap.tsx +++ b/deploy-web/src/components/providers/ProviderMap.tsx @@ -2,7 +2,7 @@ import { ApiProviderList } from "@src/types/provider"; import { ComposableMap, Geographies, Geography, Marker, Point, ZoomableGroup } from "react-simple-maps"; import { useState } from "react"; -import { CustomNoDivTooltip, CustomTooltip } from "../../components/shared/CustomTooltip"; +import { CustomNoDivTooltip, CustomTooltip } from "../shared/CustomTooltip"; import Link from "next/link"; import { UrlService } from "@src/utils/urlUtils"; import { useTheme } from "next-themes"; diff --git a/deploy-web/src/app/providers/[owner]/raw/ProviderRawData.tsx b/deploy-web/src/components/providers/ProviderRawData.tsx similarity index 83% rename from deploy-web/src/app/providers/[owner]/raw/ProviderRawData.tsx rename to deploy-web/src/components/providers/ProviderRawData.tsx index b191ed3ac..79c0cd3e9 100644 --- a/deploy-web/src/app/providers/[owner]/raw/ProviderRawData.tsx +++ b/deploy-web/src/components/providers/ProviderRawData.tsx @@ -5,8 +5,10 @@ import { useWallet } from "@src/context/WalletProvider"; import { ClientProviderDetailWithStatus } from "@src/types/provider"; import { useProviderDetail, useProviderStatus } from "@src/queries/useProvidersQuery"; import { DynamicReactJson } from "@src/components/shared/DynamicJsonView"; -import { PageContainer } from "@src/components/shared/PageContainer"; -import ProviderDetailLayout, { ProviderDetailTabs } from "../ProviderDetailLayout"; +import ProviderDetailLayout, { ProviderDetailTabs } from "./ProviderDetailLayout"; +import Layout from "../layout/Layout"; +import { CustomNextSeo } from "../shared/CustomNextSeo"; +import { UrlService } from "@src/utils/urlUtils"; type Props = { owner: string; @@ -56,12 +58,12 @@ export const ProviderRawData: React.FunctionComponent<Props> = ({ owner }) => { }; return ( - <PageContainer isLoading={isLoadingLeases || isLoadingProvider || isLoadingStatus}> - {/* <CustomNextSeo title={`Provider raw data for ${owner}`} url={`https://deploy.cloudmos.io${UrlService.providerDetailRaw(owner)}`} /> */} + <Layout isLoading={isLoadingLeases || isLoadingProvider || isLoadingStatus}> + <CustomNextSeo title={`Provider raw data for ${owner}`} url={`https://deploy.cloudmos.io${UrlService.providerDetailRaw(owner)}`} /> <ProviderDetailLayout address={owner} page={ProviderDetailTabs.RAW} refresh={refresh} provider={provider as ClientProviderDetailWithStatus}> {provider && <DynamicReactJson src={JSON.parse(JSON.stringify(provider))} collapsed={1} />} </ProviderDetailLayout> - </PageContainer> + </Layout> ); }; diff --git a/deploy-web/src/app/providers/[owner]/ProviderSpecs.tsx b/deploy-web/src/components/providers/ProviderSpecs.tsx similarity index 100% rename from deploy-web/src/app/providers/[owner]/ProviderSpecs.tsx rename to deploy-web/src/components/providers/ProviderSpecs.tsx diff --git a/deploy-web/src/app/providers/[owner]/ProviderSummary.tsx b/deploy-web/src/components/providers/ProviderSummary.tsx similarity index 98% rename from deploy-web/src/app/providers/[owner]/ProviderSummary.tsx rename to deploy-web/src/components/providers/ProviderSummary.tsx index 94f3936d4..c51b5fcd7 100644 --- a/deploy-web/src/app/providers/[owner]/ProviderSummary.tsx +++ b/deploy-web/src/components/providers/ProviderSummary.tsx @@ -1,7 +1,7 @@ "use client"; import { useLocalNotes } from "@src/context/LocalNoteProvider"; import { ApiProviderList, ClientProviderDetailWithStatus } from "@src/types/provider"; -import { ProviderMap } from "../ProviderMap"; +import { ProviderMap } from "./ProviderMap"; import { Card, CardContent } from "@src/components/ui/card"; import { LabelValue } from "@src/components/shared/LabelValue"; import { Address } from "@src/components/shared/Address"; diff --git a/deploy-web/src/app/providers/ProviderTable.tsx b/deploy-web/src/components/providers/ProviderTable.tsx similarity index 100% rename from deploy-web/src/app/providers/ProviderTable.tsx rename to deploy-web/src/components/providers/ProviderTable.tsx diff --git a/deploy-web/src/app/providers/ProviderTableRow.tsx b/deploy-web/src/components/providers/ProviderTableRow.tsx similarity index 96% rename from deploy-web/src/app/providers/ProviderTableRow.tsx rename to deploy-web/src/components/providers/ProviderTableRow.tsx index 200b3d41b..bb7af4ff0 100644 --- a/deploy-web/src/app/providers/ProviderTableRow.tsx +++ b/deploy-web/src/components/providers/ProviderTableRow.tsx @@ -1,16 +1,16 @@ "use client"; import { ClientProviderList } from "@src/types/provider"; import { useLocalNotes } from "@src/context/LocalNoteProvider"; -import { FavoriteButton } from "../../components/shared/FavoriteButton"; -import { AuditorButton } from "../../components/providers/AuditorButton"; +import { FavoriteButton } from "../shared/FavoriteButton"; +import { AuditorButton } from "./AuditorButton"; import { bytesToShrink } from "@src/utils/unitUtils"; import { roundDecimal } from "@src/utils/mathHelpers"; import { CapacityIcon } from "./CapacityIcon"; -import { CustomTooltip } from "../../components/shared/CustomTooltip"; +import { CustomTooltip } from "../shared/CustomTooltip"; import { getSplitText } from "@src/hooks/useShortText"; import { useRouter } from "next/navigation"; import { UrlService } from "@src/utils/urlUtils"; -import { Uptime } from "../../components/providers/Uptime"; +import { Uptime } from "./Uptime"; import React from "react"; import { hasSomeParentTheClass } from "@src/utils/domUtils"; import { createFilterUnique } from "@src/utils/array"; @@ -183,7 +183,9 @@ export const ProviderListRow: React.FunctionComponent<Props> = ({ provider }) => </div> } > - <Badge>{`+${gpuModels.length - 2}`}</Badge> + <div> + <Badge>{`+${gpuModels.length - 2}`}</Badge> + </div> </CustomTooltip> )} </div> diff --git a/deploy-web/src/components/sdl/EditDescriptionForm.tsx b/deploy-web/src/components/sdl/EditDescriptionForm.tsx index bbd3659cb..408442442 100644 --- a/deploy-web/src/components/sdl/EditDescriptionForm.tsx +++ b/deploy-web/src/components/sdl/EditDescriptionForm.tsx @@ -1,10 +1,13 @@ +"use client"; import { ReactNode, useRef, useState } from "react"; -import { makeStyles } from "tss-react/mui"; import { Controller, useForm } from "react-hook-form"; -import { Box, Button, CircularProgress, InputLabel, Paper, TextareaAutosize, useTheme } from "@mui/material"; import axios from "axios"; -import { useSnackbar } from "notistack"; -import { Snackbar } from "../shared/Snackbar"; +import { FormPaper } from "./FormPaper"; +import { Button } from "../ui/button"; +import Spinner from "../shared/Spinner"; +import { Label } from "../ui/label"; +import { Textarea } from "../ui/input"; +import { useToast } from "../ui/use-toast"; type Props = { id: string; @@ -18,23 +21,11 @@ type DescriptionFormValues = { description: string; }; -const useStyles = makeStyles()(theme => ({})); - export const EditDescriptionForm: React.FunctionComponent<Props> = ({ id, description, onCancel, onSave }) => { - const { classes } = useStyles(); - const theme = useTheme(); - const formRef = useRef<HTMLFormElement>(); + const formRef = useRef<HTMLFormElement>(null); const [isSaving, setIsSaving] = useState(false); - const { enqueueSnackbar } = useSnackbar(); - const { - handleSubmit, - reset, - control, - formState: { errors }, - trigger, - watch, - setValue - } = useForm<DescriptionFormValues>({ + const { toast } = useToast(); + const { handleSubmit, control } = useForm<DescriptionFormValues>({ defaultValues: { description: description || "" } @@ -47,42 +38,45 @@ export const EditDescriptionForm: React.FunctionComponent<Props> = ({ id, descri description: data.description }); - enqueueSnackbar(<Snackbar title="Description saved!" iconVariant="success" />, { - variant: "success" - }); + toast({ title: "Description saved!", variant: "success" }); + // enqueueSnackbar(<Snackbar title="Description saved!" iconVariant="success" />, { + // variant: "success" + // }); onSave(data.description); }; return ( - <Paper elevation={1} sx={{ padding: "1rem", marginTop: "1rem" }}> + <FormPaper className="mt-4"> <form onSubmit={handleSubmit(onSubmit)} ref={formRef} autoComplete="off"> <Controller control={control} name={`description`} render={({ field }) => ( - <Box sx={{ marginTop: ".5rem" }}> - <InputLabel sx={{ marginBottom: ".5rem" }}>Description</InputLabel> - <TextareaAutosize + <div> + <Label>Description</Label> + <Textarea aria-label="Description" - minRows={10} + rows={10} placeholder="Write your guide on how to use this template here!" - style={{ width: "100%", padding: ".5rem 1rem", fontFamily: "inherit", fontSize: ".8rem", resize: "vertical" }} + className="mt-2 w-full px-4 py-2 text-sm" value={field.value} spellCheck={false} onChange={field.onChange} /> - </Box> + </div> )} /> - <Box sx={{ display: "flex", alignItems: "center", justifyContent: "flex-end", marginTop: ".5rem" }}> - <Button onClick={onCancel}>Cancel</Button> - <Button color="secondary" variant="contained" type="submit" sx={{ marginLeft: "1rem" }}> - {isSaving ? <CircularProgress size="1rem" sx={{ color: theme.palette.secondary.contrastText }} /> : "Save"} + <div className="flex mt-2 items-center justify-end space-x-4"> + <Button onClick={onCancel} variant="ghost"> + Cancel + </Button> + <Button color="secondary" variant="default" type="submit"> + {isSaving ? <Spinner size="small" /> : "Save"} </Button> - </Box> + </div> </form> - </Paper> + </FormPaper> ); }; diff --git a/deploy-web/src/components/sdl/EnvFormModal.tsx b/deploy-web/src/components/sdl/EnvFormModal.tsx index 92024fd71..bcaefce04 100644 --- a/deploy-web/src/components/sdl/EnvFormModal.tsx +++ b/deploy-web/src/components/sdl/EnvFormModal.tsx @@ -122,7 +122,12 @@ export const EnvFormModal: React.FunctionComponent<Props> = ({ control, serviceI /> </div> - <div className={cn("flex w-[50px] flex-col items-start pl-2", { ["justify-between"]: envIndex > 0, ["justify-end"]: envIndex === 0 || !hasSecretOption })}> + <div + className={cn("flex w-[50px] flex-col items-start pl-2", { + ["justify-between"]: envIndex > 0, + ["justify-end"]: envIndex === 0 || !hasSecretOption + })} + > {envIndex > 0 && ( <Button onClick={() => removeEnv(envIndex)} size="icon" variant="ghost"> <Bin /> @@ -146,7 +151,7 @@ export const EnvFormModal: React.FunctionComponent<Props> = ({ control, serviceI </> } > - <Switch checked={field.value || false} onCheckedChange={field.onChange} color="primary" /> + <Switch checked={field.value || false} onCheckedChange={field.onChange} /> </CustomTooltip> )} /> diff --git a/deploy-web/src/components/sdl/ExposeFormModal.tsx b/deploy-web/src/components/sdl/ExposeFormModal.tsx index f2c08938e..2b2f6c9e5 100644 --- a/deploy-web/src/components/sdl/ExposeFormModal.tsx +++ b/deploy-web/src/components/sdl/ExposeFormModal.tsx @@ -121,7 +121,7 @@ export const ExposeFormModal: React.FunctionComponent<Props> = ({ control, servi const currentExpose = _expose[expIndex]; return ( - <FormPaper key={exp.id} className={cn({ ["mb-4"]: expIndex + 1 !== expose.length })} contentClassName="flex bg-popover"> + <FormPaper key={exp.id} className={cn("bg-popover", { ["mb-4"]: expIndex + 1 !== expose.length })} contentClassName="flex"> <div className="flex-grow"> <div className="mb-4 grid grid-cols-1 gap-2 sm:grid-cols-4"> <div> @@ -300,18 +300,13 @@ export const ExposeFormModal: React.FunctionComponent<Props> = ({ control, servi </div> <div> - <HttpOptionsFormControl - control={control} - serviceIndex={serviceIndex} - exposeIndex={expIndex} - services={services} - /> + <HttpOptionsFormControl control={control} serviceIndex={serviceIndex} exposeIndex={expIndex} services={services} /> </div> </div> {expIndex !== 0 && ( <div className="pl-2"> - <Button onClick={() => removeExpose(expIndex)} size="icon"> + <Button onClick={() => removeExpose(expIndex)} size="icon" variant="ghost"> <Bin /> </Button> </div> diff --git a/deploy-web/src/components/sdl/GpuFormControl.tsx b/deploy-web/src/components/sdl/GpuFormControl.tsx index b5cacafa5..0cdb58122 100644 --- a/deploy-web/src/components/sdl/GpuFormControl.tsx +++ b/deploy-web/src/components/sdl/GpuFormControl.tsx @@ -17,7 +17,7 @@ import { GpuVendor } from "@src/types/gpu"; import { Button } from "../ui/button"; import FormControl from "@mui/material/FormControl"; import InputLabel from "@mui/material/InputLabel"; -import { default as MuiSelect } from "@mui/material/Select/Select"; +import { default as MuiSelect } from "@mui/material/Select"; import IconButton from "@mui/material/IconButton"; import MenuItem from "@mui/material/MenuItem"; @@ -76,7 +76,7 @@ export const GpuFormControl: React.FunctionComponent<Props> = ({ gpuModels, cont <div className="flex items-center"> <div className="flex items-center"> <div className="flex items-center"> - <MdSpeed className="mr-2 text-muted-foreground" /> + <MdSpeed className="mr-2 text-2xl text-muted-foreground" /> <strong>GPU</strong> <CustomTooltip @@ -386,7 +386,7 @@ export const GpuFormControl: React.FunctionComponent<Props> = ({ gpuModels, cont ) : ( <div className="ml-4 flex items-center"> <Spinner /> - <span className="ml-2 text-sm text-muted-foreground whitespace-nowrap">Loading GPU models...</span> + <span className="ml-2 whitespace-nowrap text-sm text-muted-foreground">Loading GPU models...</span> </div> )} </div> diff --git a/deploy-web/src/components/sdl/ImportSdlModal.tsx b/deploy-web/src/components/sdl/ImportSdlModal.tsx index 4f276a669..4d432647e 100644 --- a/deploy-web/src/components/sdl/ImportSdlModal.tsx +++ b/deploy-web/src/components/sdl/ImportSdlModal.tsx @@ -1,16 +1,17 @@ +"use client"; import { ReactNode, useEffect, useState } from "react"; -import { Popup } from "../shared/Popup"; -import { Alert, Box, Typography, useTheme } from "@mui/material"; -import { useSnackbar } from "notistack"; import Editor from "@monaco-editor/react"; import { Timer } from "@src/utils/timer"; import { importSimpleSdl } from "@src/utils/sdl/sdlImport"; import { UseFormSetValue } from "react-hook-form"; import { SdlBuilderFormValues, Service } from "@src/types"; -import { Snackbar } from "../shared/Snackbar"; -import ArrowDownwardIcon from "@mui/icons-material/ArrowDownward"; import { event } from "nextjs-google-analytics"; import { AnalyticsEvents } from "@src/utils/analytics"; +import { useToast } from "@src/components/ui/use-toast"; +import { Popup } from "@src/components/shared/Popup"; +import { useTheme } from "next-themes"; +import { ArrowDown } from "iconoir-react"; +import { Alert } from "@src/components/ui/alert"; type Props = { setValue: UseFormSetValue<SdlBuilderFormValues>; @@ -19,16 +20,16 @@ type Props = { }; export const ImportSdlModal: React.FunctionComponent<Props> = ({ onClose, setValue }) => { - const theme = useTheme(); - const [sdl, setSdl] = useState(""); - const [parsingError, setParsingError] = useState(null); - const { enqueueSnackbar } = useSnackbar(); + const [sdl, setSdl] = useState<string | undefined>(""); + const [parsingError, setParsingError] = useState<string | null>(null); + const { toast } = useToast(); + const { theme } = useTheme(); useEffect(() => { const timer = Timer(500); timer.start().then(() => { - createAndValidateSdl(sdl); + createAndValidateSdl(sdl || ""); }); return () => { @@ -62,17 +63,18 @@ export const ImportSdlModal: React.FunctionComponent<Props> = ({ onClose, setVal }; const onImport = () => { - const result = createAndValidateSdl(sdl); + const result = createAndValidateSdl(sdl || ""); console.log(result); if (!result) return; setValue("services", result as Service[]); - enqueueSnackbar(<Snackbar title="Import success!" iconVariant="success" />, { - variant: "success", - autoHideDuration: 4000 - }); + toast({ title: "Import success!", variant: "success" }); + // enqueueSnackbar(<Snackbar title="Import success!" iconVariant="success" />, { + // variant: "success", + // autoHideDuration: 4000 + // }); event(AnalyticsEvents.IMPORT_SDL, { category: "sdl_builder", @@ -99,7 +101,7 @@ export const ImportSdlModal: React.FunctionComponent<Props> = ({ onClose, setVal { label: "Import", color: "secondary", - variant: "contained", + variant: "default", side: "right", disabled: !sdl || !!parsingError, onClick: onImport @@ -109,28 +111,14 @@ export const ImportSdlModal: React.FunctionComponent<Props> = ({ onClose, setVal maxWidth="md" enableCloseOnBackdropClick > - <Typography - variant="h6" - sx={{ - marginBottom: ".5rem", - color: theme.palette.mode === "dark" ? theme.palette.grey[400] : theme.palette.grey[600], - display: "flex", - alignItems: "center" - }} - > - Paste your sdl here to import <ArrowDownwardIcon sx={{ marginLeft: "1rem" }} /> - </Typography> - <Box sx={{ marginBottom: ".5rem" }}> - <Editor - height="500px" - defaultLanguage="yaml" - value={sdl} - onChange={value => setSdl(value)} - theme={theme.palette.mode === "dark" ? "vs-dark" : "light"} - /> - </Box> + <h6 className="mb-4 flex items-center text-muted-foreground"> + Paste your sdl here to import <ArrowDown className="ml-4 text-sm" /> + </h6> + <div className="mb-2"> + <Editor height="500px" defaultLanguage="yaml" value={sdl} onChange={value => setSdl(value)} theme={theme === "dark" ? "vs-dark" : "light"} /> + </div> {parsingError && ( - <Alert severity="error" variant="outlined"> + <Alert className="mt-4" variant="destructive"> {parsingError} </Alert> )} diff --git a/deploy-web/src/components/sdl/PreviewSdl.tsx b/deploy-web/src/components/sdl/PreviewSdl.tsx index 917e64661..122ae8708 100644 --- a/deploy-web/src/components/sdl/PreviewSdl.tsx +++ b/deploy-web/src/components/sdl/PreviewSdl.tsx @@ -1,11 +1,12 @@ +"use client" import { ReactNode } from "react"; -import { Popup } from "../shared/Popup"; -import { Box, Button, IconButton, Typography, useTheme } from "@mui/material"; -import { useSnackbar } from "notistack"; import Editor from "@monaco-editor/react"; import { copyTextToClipboard } from "@src/utils/copyClipboard"; -import FileCopy from "@mui/icons-material/FileCopy"; -import { Snackbar } from "../shared/Snackbar"; +import { useToast } from "@src/components/ui/use-toast"; +import { Popup } from "@src/components/shared/Popup"; +import { Button } from "@src/components/ui/button"; +import { Copy } from "iconoir-react"; +import { useTheme } from "next-themes"; type Props = { sdl: string; @@ -14,15 +15,16 @@ type Props = { }; export const PreviewSdl: React.FunctionComponent<Props> = ({ sdl, onClose }) => { - const theme = useTheme(); - const { enqueueSnackbar } = useSnackbar(); + const { toast } = useToast(); + const { theme } = useTheme(); const onCopyClick = () => { copyTextToClipboard(sdl); - enqueueSnackbar(<Snackbar title="SDL copied to clipboard!" iconVariant="success" />, { - variant: "success", - autoHideDuration: 3000 - }); + toast({title: "SDL copied to clipboard!", variant: "success"}); + // enqueueSnackbar(<Snackbar title="SDL copied to clipboard!" iconVariant="success" />, { + // variant: "success", + // autoHideDuration: 3000 + // }); }; return ( @@ -44,14 +46,15 @@ export const PreviewSdl: React.FunctionComponent<Props> = ({ sdl, onClose }) => maxWidth="md" enableCloseOnBackdropClick > - <Box sx={{ display: "flex", alignItems: "center", marginBottom: ".5rem" }}> - <Button color="secondary" variant="contained" endIcon={<FileCopy fontSize="small" />} onClick={onCopyClick}> + <div className="flex items-center mb-4"> + <Button color="secondary" variant="default" onClick={onCopyClick} size="sm"> Copy the SDL + <Copy className="ml-2 text-sm" /> </Button> - </Box> - <Box sx={{ marginBottom: ".5rem" }}> - <Editor height="500px" defaultLanguage="yaml" value={sdl} theme={theme.palette.mode === "dark" ? "vs-dark" : "light"} /> - </Box> + </div> + <div className="mb-2"> + <Editor height="500px" defaultLanguage="yaml" value={sdl} theme={theme === "dark" ? "vs-dark" : "light"} /> + </div> </Popup> ); }; diff --git a/deploy-web/src/components/sdl/RentGpusForm.tsx b/deploy-web/src/components/sdl/RentGpusForm.tsx index 2bb65bdcd..ba3b8814a 100644 --- a/deploy-web/src/components/sdl/RentGpusForm.tsx +++ b/deploy-web/src/components/sdl/RentGpusForm.tsx @@ -25,7 +25,7 @@ import { generateCertificate } from "@src/utils/certificateUtils"; import { TransactionMessageData } from "@src/utils/TransactionMessageData"; import { updateWallet } from "@src/utils/walletUtils"; import { saveDeploymentManifestAndName } from "@src/utils/deploymentLocalDataUtils"; -import { DeploymentDepositModal } from "../deploymentDetail/DeploymentDepositModal"; +import { DeploymentDepositModal } from "../deployments/DeploymentDepositModal"; import { LinkTo } from "../shared/LinkTo"; import { PrerequisiteList } from "../shared/PrerequisiteList"; import { ProviderAttributeSchemaDetailValue } from "@src/types/providerAttributes"; diff --git a/deploy-web/src/components/sdl/SaveTemplateModal.tsx b/deploy-web/src/components/sdl/SaveTemplateModal.tsx index 638e9e0a6..0998bf67c 100644 --- a/deploy-web/src/components/sdl/SaveTemplateModal.tsx +++ b/deploy-web/src/components/sdl/SaveTemplateModal.tsx @@ -1,17 +1,19 @@ +"use client"; import { Dispatch, ReactNode, SetStateAction, useEffect, useRef, useState } from "react"; -import { makeStyles } from "tss-react/mui"; -import { Popup } from "../shared/Popup"; -import { Alert, Box, FormControlLabel, Radio, RadioGroup, TextField, useTheme } from "@mui/material"; -import { useSnackbar } from "notistack"; import { Controller, useForm } from "react-hook-form"; -import { ITemplate, SdlSaveTemplateFormValues, Service } from "@src/types"; +import { EnvironmentVariable, ITemplate, SdlSaveTemplateFormValues, Service } from "@src/types"; import { useSaveUserTemplate } from "@src/queries/useTemplateQuery"; -import { Snackbar } from "../shared/Snackbar"; -import { MustConnect } from "../shared/MustConnect"; import { useCustomUser } from "@src/hooks/useCustomUser"; import { getShortText } from "@src/hooks/useShortText"; import { event } from "nextjs-google-analytics"; import { AnalyticsEvents } from "@src/utils/analytics"; +import { useToast } from "@src/components/ui/use-toast"; +import { Popup } from "@src/components/shared/Popup"; +import { Alert } from "@src/components/ui/alert"; +import { MustConnect } from "@src/components/shared/MustConnect"; +import TextField from "@mui/material/TextField"; +import { RadioGroup, RadioGroupItem } from "@src/components/ui/radio-group"; +import { Label } from "@src/components/ui/label"; type Props = { services: Service[]; @@ -22,20 +24,10 @@ type Props = { children?: ReactNode; }; -const useStyles = makeStyles()(theme => ({ - formControl: { - marginBottom: theme.spacing(1.5) - }, - textField: { - width: "100%" - } -})); - export const SaveTemplateModal: React.FunctionComponent<Props> = ({ onClose, getTemplateData, templateMetadata, setTemplateMetadata, services }) => { - const { classes } = useStyles(); - const [publicEnvs, setPublicEnvs] = useState([]); - const { enqueueSnackbar } = useSnackbar(); - const formRef = useRef<HTMLFormElement>(); + const [publicEnvs, setPublicEnvs] = useState<EnvironmentVariable[]>([]); + const { toast } = useToast(); + const formRef = useRef<HTMLFormElement>(null); const { user, isLoading: isLoadingUser } = useCustomUser(); const isRestricted = !isLoadingUser && !user; const isCurrentUserTemplate = !isRestricted && user?.sub === templateMetadata?.userId; @@ -48,8 +40,9 @@ export const SaveTemplateModal: React.FunctionComponent<Props> = ({ onClose, get }); useEffect(() => { - const envs = services.some(s => s.env.some(e => !e.isSecret)) ? services.reduce((cur, prev) => cur.concat([...prev.env.filter(e => !e.isSecret)]), []) : []; - console.log(envs); + const envs = services.some(s => s.env?.some(e => !e.isSecret)) + ? services.reduce((cur: EnvironmentVariable[], prev) => cur.concat([...(prev.env?.filter(e => !e.isSecret) as EnvironmentVariable[])]), []) + : []; setPublicEnvs(envs); if (templateMetadata && isCurrentUserTemplate) { @@ -66,13 +59,11 @@ export const SaveTemplateModal: React.FunctionComponent<Props> = ({ onClose, get const newTemplateMetadata = { ...templateMetadata, title: data.title, isPublic: data.visibility !== "private" }; if (!isCurrentUserTemplate) { newTemplateMetadata.username = user.username; - newTemplateMetadata.userId = user.sub; + newTemplateMetadata.userId = user.sub || ""; } setTemplateMetadata(newTemplateMetadata); - enqueueSnackbar(<Snackbar title="Template saved!" iconVariant="success" />, { - variant: "success" - }); + toast({ title: "Template saved!", variant: "success" }); if (newTemplateMetadata.id) { event(AnalyticsEvents.UPDATE_SDL_TEMPLATE, { @@ -90,7 +81,7 @@ export const SaveTemplateModal: React.FunctionComponent<Props> = ({ onClose, get }; const onSave = () => { - formRef.current.dispatchEvent(new Event("submit", { cancelable: true, bubbles: true })); + formRef.current?.dispatchEvent(new Event("submit", { cancelable: true, bubbles: true })); }; return ( @@ -110,7 +101,7 @@ export const SaveTemplateModal: React.FunctionComponent<Props> = ({ onClose, get { label: isCurrentUserTemplate ? "Save" : "Save As", color: "secondary", - variant: "contained", + variant: "default", side: "right", disabled: isRestricted, onClick: onSave @@ -120,7 +111,7 @@ export const SaveTemplateModal: React.FunctionComponent<Props> = ({ onClose, get maxWidth="xs" enableCloseOnBackdropClick > - <Box sx={{ paddingTop: ".5rem" }}> + <div className="pt-2"> {isRestricted ? ( <MustConnect message="To save a template" /> ) : ( @@ -136,9 +127,8 @@ export const SaveTemplateModal: React.FunctionComponent<Props> = ({ onClose, get label="Title" error={!!fieldState.error} helperText={fieldState.error?.message} - className={classes.formControl} + className="mb-4" fullWidth - color="secondary" size="small" value={field.value || ""} onChange={event => field.onChange(event.target.value)} @@ -150,28 +140,34 @@ export const SaveTemplateModal: React.FunctionComponent<Props> = ({ onClose, get control={control} name={`visibility`} render={({ field }) => ( - <RadioGroup defaultValue="private" value={field.value} onChange={field.onChange}> - <FormControlLabel value="private" control={<Radio color="secondary" />} label="Private" /> - <FormControlLabel value="public" control={<Radio color="secondary" />} label="Public" /> + <RadioGroup defaultValue="private" value={field.value} onValueChange={field.onChange} className="space-y-2"> + <div className="flex items-center space-x-2"> + <RadioGroupItem value="private" id="private" /> + <Label htmlFor="private">Private</Label> + </div> + <div className="flex items-center space-x-2"> + <RadioGroupItem value="public" id="public" /> + <Label htmlFor="public">Public</Label> + </div> </RadioGroup> )} /> {publicEnvs.length > 0 && ( - <Alert severity="warning" sx={{ marginTop: "1rem", maxHeight: "150px", overflowY: "auto" }}> + <Alert variant="warning" className="mt-4 max-h-[150px] overflow-y-auto"> You have {publicEnvs.length} public environment variables. Are you sure you don't need to hide them as secret? - <Box component="ul" sx={{ padding: 0, wordBreak: "break-all" }}> + <ul className="break-all p-0"> {publicEnvs.map((e, i) => ( <li key={i}> {e.key}={getShortText(e.value, 30)} </li> ))} - </Box> + </ul> </Alert> )} </form> )} - </Box> + </div> </Popup> ); }; diff --git a/deploy-web/src/components/sdl/SimpleSdlBuilderForm.tsx b/deploy-web/src/components/sdl/SimpleSdlBuilderForm.tsx index 721d0014c..910e55893 100644 --- a/deploy-web/src/components/sdl/SimpleSdlBuilderForm.tsx +++ b/deploy-web/src/components/sdl/SimpleSdlBuilderForm.tsx @@ -1,55 +1,49 @@ -import { Alert, Box, Button, CircularProgress, Typography } from "@mui/material"; +"use client"; import { useForm, useFieldArray } from "react-hook-form"; import { useEffect, useRef, useState } from "react"; import { nanoid } from "nanoid"; import { ITemplate, SdlBuilderFormValues, Service } from "@src/types"; import { generateSdl } from "@src/utils/sdl/sdlGenerator"; import { defaultService } from "@src/utils/sdl/data"; -import { SimpleServiceFormControl } from "./SimpleServiceFormControl"; -import { ImportSdlModal } from "./ImportSdlModal"; -import { useRouter } from "next/router"; +import { useRouter, useSearchParams } from "next/navigation"; import axios from "axios"; -import ArrowForwardIcon from "@mui/icons-material/ArrowForward"; import { importSimpleSdl } from "@src/utils/sdl/sdlImport"; import Link from "next/link"; import { UrlService } from "@src/utils/urlUtils"; -import { SaveTemplateModal } from "./SaveTemplateModal"; -import { useSnackbar } from "notistack"; -import { Snackbar } from "../shared/Snackbar"; import { event } from "nextjs-google-analytics"; import { AnalyticsEvents } from "@src/utils/analytics"; -import { memoryUnits, storageUnits } from "../shared/akash/units"; import sdlStore from "@src/store/sdlStore"; import { RouteStepKeys } from "@src/utils/constants"; import { useAtom } from "jotai"; -import { PreviewSdl } from "./PreviewSdl"; import { useGpuModels } from "@src/queries/useGpuQuery"; +import { useToast } from "@src/components/ui/use-toast"; +import { memoryUnits, storageUnits } from "@src/components/shared/akash/units"; +import { SimpleServiceFormControl } from "@src/components/sdl/SimpleServiceFormControl"; +import { Button } from "@src/components/ui/button"; +import { Alert } from "@src/components/ui/alert"; +import Spinner from "@src/components/shared/Spinner"; +import { NavArrowRight } from "iconoir-react"; +import { ImportSdlModal } from "./ImportSdlModal"; +import { PreviewSdl } from "./PreviewSdl"; +import { SaveTemplateModal } from "./SaveTemplateModal"; type Props = {}; export const SimpleSDLBuilderForm: React.FunctionComponent<Props> = ({}) => { const [error, setError] = useState(null); - const [templateMetadata, setTemplateMetadata] = useState<ITemplate>(null); - const [serviceCollapsed, setServiceCollapsed] = useState([]); + const [templateMetadata, setTemplateMetadata] = useState<ITemplate | null>(null); + const [serviceCollapsed, setServiceCollapsed] = useState<number[]>([]); const [isLoadingTemplate, setIsLoadingTemplate] = useState(false); const [isSavingTemplate, setIsSavingTemplate] = useState(false); const [isImportingSdl, setIsImportingSdl] = useState(false); const [isPreviewingSdl, setIsPreviewingSdl] = useState(false); - const [sdlResult, setSdlResult] = useState<string>(null); - const formRef = useRef<HTMLFormElement>(); + const [sdlResult, setSdlResult] = useState<string | null>(null); + const formRef = useRef<HTMLFormElement>(null); const [, setDeploySdl] = useAtom(sdlStore.deploySdl); const [sdlBuilderSdl, setSdlBuilderSdl] = useAtom(sdlStore.sdlBuilderSdl); const { data: gpuModels } = useGpuModels(); - const { enqueueSnackbar } = useSnackbar(); - const { - handleSubmit, - reset, - control, - formState: { isValid }, - trigger, - watch, - setValue - } = useForm<SdlBuilderFormValues>({ + const { toast } = useToast(); + const { handleSubmit, reset, control, trigger, watch, setValue } = useForm<SdlBuilderFormValues>({ defaultValues: { services: [{ ...defaultService }] } @@ -65,6 +59,8 @@ export const SimpleSDLBuilderForm: React.FunctionComponent<Props> = ({}) => { }); const { services: _services } = watch(); const router = useRouter(); + const searchParams = useSearchParams(); + const templateQueryId = searchParams?.get("id"); useEffect(() => { if (sdlBuilderSdl && sdlBuilderSdl.services) { @@ -74,18 +70,18 @@ export const SimpleSDLBuilderForm: React.FunctionComponent<Props> = ({}) => { // Load the template from query string on mount useEffect(() => { - if ((router.query.id && !templateMetadata) || (router.query.id && templateMetadata?.id !== router.query.id)) { + if ((templateQueryId && !templateMetadata) || (templateQueryId && templateMetadata?.id !== templateQueryId)) { // Load user template - loadTemplate(router.query.id as string); - } else if (!router.query.id && templateMetadata) { + loadTemplate(templateQueryId as string); + } else if (!templateQueryId && templateMetadata) { setTemplateMetadata(null); reset(); } - }, [router.query.id, templateMetadata]); + }, [templateQueryId, templateMetadata]); useEffect(() => { if (_services) { - setSdlBuilderSdl({ services: _services }); + setSdlBuilderSdl({ services: _services as Service[] }); } }, [_services]); @@ -104,9 +100,7 @@ export const SimpleSDLBuilderForm: React.FunctionComponent<Props> = ({}) => { setServiceCollapsed(services.map((x, i) => i)); setTemplateMetadata(template); } catch (error) { - enqueueSnackbar(<Snackbar title="Error fetching template." iconVariant="error" />, { - variant: "error" - }); + toast({ title: "Error fetching template.", variant: "destructive" }); setIsLoadingTemplate(false); } @@ -157,7 +151,7 @@ export const SimpleSDLBuilderForm: React.FunctionComponent<Props> = ({}) => { setError(null); try { - const sdl = generateSdl(_services); + const sdl = generateSdl(_services as Service[]); setSdlResult(sdl); setIsPreviewingSdl(true); @@ -171,24 +165,24 @@ export const SimpleSDLBuilderForm: React.FunctionComponent<Props> = ({}) => { }; const getTemplateData = () => { - const sdl = generateSdl(_services); + const sdl = generateSdl(_services as Service[]); const template: Partial<ITemplate> = { - id: templateMetadata?.id || null, + id: templateMetadata?.id || undefined, sdl, - cpu: _services.map(s => s.profile.cpu * 1000).reduce((a, b) => a + b, 0), + cpu: _services?.map(s => (s.profile?.cpu || 0) * 1000).reduce((a, b) => a + b, 0), ram: _services - .map(s => { - const ramUnit = memoryUnits.find(x => x.suffix === s.profile.ramUnit); + ?.map(s => { + const ramUnit = memoryUnits.find(x => x.suffix === s.profile?.ramUnit); - return s.profile.ram * ramUnit.value; + return (s.profile?.ram || 0) * (ramUnit?.value || 0); }) .reduce((a, b) => a + b, 0), storage: _services - .map(s => { - const ephemeralStorageUnit = storageUnits.find(x => x.suffix === s.profile.ramUnit); - const peristentStorageUnit = storageUnits.find(x => x.suffix === s.profile.persistentStorageUnit); - const ephemeralStorage = s.profile.storage + ephemeralStorageUnit.value; - const persistentStorage = s.profile.hasPersistentStorage ? s.profile.persistentStorage + peristentStorageUnit.value : 0; + ?.map(s => { + const ephemeralStorageUnit = storageUnits.find(x => x.suffix === s.profile?.ramUnit); + const peristentStorageUnit = storageUnits.find(x => x.suffix === s.profile?.persistentStorageUnit); + const ephemeralStorage = (s.profile?.storage || 0) + (ephemeralStorageUnit?.value || 0); + const persistentStorage = s.profile?.hasPersistentStorage ? (s.profile?.persistentStorage || 0) + (peristentStorageUnit?.value || 0) : 0; return ephemeralStorage + persistentStorage; }) @@ -200,21 +194,21 @@ export const SimpleSDLBuilderForm: React.FunctionComponent<Props> = ({}) => { return ( <> {isImportingSdl && <ImportSdlModal onClose={() => setIsImportingSdl(false)} setValue={setValue} />} - {isPreviewingSdl && <PreviewSdl onClose={() => setIsPreviewingSdl(false)} sdl={sdlResult} />} + {isPreviewingSdl && <PreviewSdl onClose={() => setIsPreviewingSdl(false)} sdl={sdlResult || ""} />} {isSavingTemplate && ( <SaveTemplateModal onClose={() => setIsSavingTemplate(false)} getTemplateData={getTemplateData} - templateMetadata={templateMetadata} + templateMetadata={templateMetadata as ITemplate} setTemplateMetadata={setTemplateMetadata} - services={_services} + services={_services as Service[]} /> )} <form onSubmit={handleSubmit(onSubmit)} ref={formRef} autoComplete="off"> {templateMetadata && ( - <Box sx={{ display: "flex", alignItems: "center" }}> - <Typography variant="body1"> + <div className="flex items-center"> + <p> {templateMetadata.title} by  {templateMetadata.username && ( <span @@ -228,13 +222,12 @@ export const SimpleSDLBuilderForm: React.FunctionComponent<Props> = ({}) => { <Link href={UrlService.userProfile(templateMetadata.username)}>{templateMetadata.username}</Link> </span> )} - </Typography> + </p> - <Box sx={{ marginLeft: "1.5rem" }}> - <Box - href={UrlService.template(router.query.id as string)} - component={Link} - sx={{ display: "inline-flex", alignItems: "center", cursor: "pointer" }} + <div className="ml-6"> + <Link + href={UrlService.template(templateQueryId as string)} + className="inline-flex cursor-pointer items-center" onClick={() => { event(AnalyticsEvents.CLICK_VIEW_TEMPLATE, { category: "sdl_builder", @@ -242,29 +235,30 @@ export const SimpleSDLBuilderForm: React.FunctionComponent<Props> = ({}) => { }); }} > - View template <ArrowForwardIcon sx={{ marginLeft: ".5rem" }} fontSize="small" /> - </Box> - </Box> - </Box> + View template <NavArrowRight className="ml-2 text-sm" /> + </Link> + </div> + </div> )} - <Box sx={{ paddingTop: "1rem", display: "flex", alignItems: "center", justifyContent: "space-between" }}> - <Box sx={{ display: "flex", alignItems: "center" }}> - <Button color="secondary" variant="contained" type="submit"> + <div className="flex items-center justify-between pt-4"> + <div className="flex items-center"> + <Button color="secondary" variant="default" type="submit"> Deploy </Button> - <Button color="secondary" variant="text" onClick={onPreviewSdlClick} sx={{ marginLeft: "1rem" }}> + <Button color="secondary" variant="text" onClick={onPreviewSdlClick} className="ml-4" type="button"> Preview </Button> - <Button color="secondary" variant="text" onClick={() => setIsImportingSdl(true)} sx={{ marginLeft: "1rem" }}> + <Button color="secondary" variant="text" onClick={() => setIsImportingSdl(true)} className="ml-4" type="button"> Import </Button> <Button variant="text" - sx={{ marginLeft: "1rem" }} + className="ml-4" + type="button" onClick={() => { event(AnalyticsEvents.RESET_SDL, { category: "sdl_builder", @@ -278,24 +272,24 @@ export const SimpleSDLBuilderForm: React.FunctionComponent<Props> = ({}) => { </Button> {isLoadingTemplate && ( - <Box sx={{ marginLeft: "1rem" }}> - <CircularProgress color="secondary" size="1.2rem" /> - </Box> + <div className="ml-4"> + <Spinner size="small" /> + </div> )} - </Box> + </div> <div> - <Button color="secondary" variant="contained" onClick={() => onSaveClick()}> + <Button color="secondary" variant="default" type="button" onClick={() => onSaveClick()}> Save </Button> </div> - </Box> + </div> {services.map((service, serviceIndex) => ( <SimpleServiceFormControl key={service.id} serviceIndex={serviceIndex} - _services={_services} + _services={_services as Service[]} setValue={setValue} control={control} trigger={trigger} @@ -307,18 +301,18 @@ export const SimpleSDLBuilderForm: React.FunctionComponent<Props> = ({}) => { ))} {error && ( - <Alert severity="error" variant="outlined" sx={{ marginTop: "1rem" }}> + <Alert variant="destructive" className="mt-4"> {error} </Alert> )} - <Box sx={{ paddingTop: "1rem", display: "flex", alignItems: "center", justifyContent: "flex-end" }}> + <div className="flex items-center justify-end pt-4"> <div> - <Button color="secondary" variant="contained" onClick={onAddService}> + <Button color="secondary" variant="default" onClick={onAddService} type="button"> Add Service </Button> </div> - </Box> + </div> </form> </> ); diff --git a/deploy-web/src/app/settings/CertificateDisplay.tsx b/deploy-web/src/components/settings/CertificateDisplay.tsx similarity index 100% rename from deploy-web/src/app/settings/CertificateDisplay.tsx rename to deploy-web/src/components/settings/CertificateDisplay.tsx diff --git a/deploy-web/src/app/settings/CertificateList.tsx b/deploy-web/src/components/settings/CertificateList.tsx similarity index 100% rename from deploy-web/src/app/settings/CertificateList.tsx rename to deploy-web/src/components/settings/CertificateList.tsx diff --git a/deploy-web/src/app/settings/ColorModeSelect.tsx b/deploy-web/src/components/settings/ColorModeSelect.tsx similarity index 100% rename from deploy-web/src/app/settings/ColorModeSelect.tsx rename to deploy-web/src/components/settings/ColorModeSelect.tsx diff --git a/deploy-web/src/app/settings/ExportCertificate.tsx b/deploy-web/src/components/settings/ExportCertificate.tsx similarity index 100% rename from deploy-web/src/app/settings/ExportCertificate.tsx rename to deploy-web/src/components/settings/ExportCertificate.tsx diff --git a/deploy-web/src/app/settings/SelectNetworkModal.tsx b/deploy-web/src/components/settings/SelectNetworkModal.tsx similarity index 100% rename from deploy-web/src/app/settings/SelectNetworkModal.tsx rename to deploy-web/src/components/settings/SelectNetworkModal.tsx diff --git a/deploy-web/src/app/settings/SettingsContainer.tsx b/deploy-web/src/components/settings/SettingsContainer.tsx similarity index 79% rename from deploy-web/src/app/settings/SettingsContainer.tsx rename to deploy-web/src/components/settings/SettingsContainer.tsx index ea0185787..b9b1a741b 100644 --- a/deploy-web/src/app/settings/SettingsContainer.tsx +++ b/deploy-web/src/components/settings/SettingsContainer.tsx @@ -1,16 +1,17 @@ "use client"; -import { SettingsForm } from "@src/app/settings/SettingsForm"; -import { ColorModeSelect } from "@src/app/settings/ColorModeSelect"; -import { PageContainer } from "@src/components/shared/PageContainer"; -import { SettingsLayout, SettingsTabs } from "@src/app/settings/SettingsLayout"; import { Fieldset } from "@src/components/shared/Fieldset"; import { useState } from "react"; import { useSelectedNetwork } from "@src/hooks/useSelectedNetwork"; import { LabelValue } from "@src/components/shared/LabelValue"; import { Button } from "@src/components/ui/button"; import { Edit } from "iconoir-react"; -import { SelectNetworkModal } from "@src/app/settings/SelectNetworkModal"; import { CertificateList } from "./CertificateList"; +import Layout from "../layout/Layout"; +import { SettingsLayout, SettingsTabs } from "./SettingsLayout"; +import { SelectNetworkModal } from "./SelectNetworkModal"; +import { SettingsForm } from "./SettingsForm"; +import { ColorModeSelect } from "./ColorModeSelect"; +import { NextSeo } from "next-seo"; type Props = {}; @@ -23,7 +24,9 @@ export const SettingsContainer: React.FunctionComponent<Props> = ({}) => { }; return ( - <PageContainer className="pt-6" isUsingSettings> + <Layout isUsingSettings> + <NextSeo title="Settings" /> + <SettingsLayout page={SettingsTabs.GENERAL} title="Settings"> {isSelectingNetwork && <SelectNetworkModal onClose={onSelectNetworkModalClose} />} <div className="grid-col-1 mb-4 grid gap-4 md:grid-cols-2"> @@ -52,6 +55,6 @@ export const SettingsContainer: React.FunctionComponent<Props> = ({}) => { <CertificateList /> </Fieldset> </SettingsLayout> - </PageContainer> + </Layout> ); }; diff --git a/deploy-web/src/app/settings/SettingsForm.tsx b/deploy-web/src/components/settings/SettingsForm.tsx similarity index 100% rename from deploy-web/src/app/settings/SettingsForm.tsx rename to deploy-web/src/components/settings/SettingsForm.tsx diff --git a/deploy-web/src/app/settings/SettingsLayout.tsx b/deploy-web/src/components/settings/SettingsLayout.tsx similarity index 100% rename from deploy-web/src/app/settings/SettingsLayout.tsx rename to deploy-web/src/components/settings/SettingsLayout.tsx diff --git a/deploy-web/src/components/shared/AddressLink.tsx b/deploy-web/src/components/shared/AddressLink.tsx index b73249fd4..20aaca928 100644 --- a/deploy-web/src/components/shared/AddressLink.tsx +++ b/deploy-web/src/components/shared/AddressLink.tsx @@ -1,15 +1,15 @@ +"use client"; import React, { ReactNode } from "react"; import Link from "next/link"; import { Address } from "./Address"; type Props = { address: string; - addressBookMode?: "always" | "never" | "alongside"; children?: ReactNode; }; -export const AddressLink: React.FunctionComponent<Props> = ({ address, addressBookMode, ...rest }) => { - let href = null; +export const AddressLink: React.FunctionComponent<Props> = ({ address }) => { + let href: string | null = null; let target = "_self"; if (address.startsWith("akashvaloper")) { href = `https://stats.akash.network/validators/${address}`; @@ -22,7 +22,7 @@ export const AddressLink: React.FunctionComponent<Props> = ({ address, addressBo if (href) { return ( <Link href={href} target={target}> - <Address address={address} addressBookMode={addressBookMode} disableTruncate /> + <Address address={address} disableTruncate /> </Link> ); } else { diff --git a/deploy-web/src/components/shared/CustomPagination.tsx b/deploy-web/src/components/shared/CustomPagination.tsx index b2236ef89..f8091fe35 100644 --- a/deploy-web/src/components/shared/CustomPagination.tsx +++ b/deploy-web/src/components/shared/CustomPagination.tsx @@ -10,7 +10,6 @@ interface Props { } export function CustomPagination({ pageIndex, totalPageCount, pageSize, setPageSize, setPageIndex }: React.PropsWithChildren<Props>) { - console.log(pageIndex) return ( <div className="flex flex-col items-center justify-between px-2 sm:flex-row"> <div className="flex items-center space-x-6 lg:space-x-8"> @@ -26,9 +25,9 @@ export function CustomPagination({ pageIndex, totalPageCount, pageSize, setPageS <SelectValue placeholder={pageSize} /> </SelectTrigger> <SelectContent side="top"> - {[10, 20, 30, 40, 50].map(pageSize => ( - <SelectItem key={pageSize} value={`${pageSize}`}> - {pageSize} + {[10, 20, 30, 40, 50].map((val, i) => ( + <SelectItem key={i} value={`${val}`}> + {val} </SelectItem> ))} </SelectContent> diff --git a/deploy-web/src/components/shared/CustomTable.tsx b/deploy-web/src/components/shared/CustomTable.tsx deleted file mode 100644 index 4bb9fa271..000000000 --- a/deploy-web/src/components/shared/CustomTable.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { darken, styled, TableHead, TableRow } from "@mui/material"; - -export const CustomTableRow = styled(TableRow)(({ theme }) => ({ - whiteSpace: "nowrap", - height: "40px", - "&:nth-of-type(odd)": { - backgroundColor: theme.palette.mode === "dark" ? darken(theme.palette.grey[700], 0.5) : theme.palette.action.hover - }, - "& td": { - border: "none" - } -})); - -export const CustomTableHeader = styled(TableHead)(({ theme }) => ({ - "& th": { - textTransform: "uppercase", - border: "none", - color: theme.palette.mode === "dark" ? theme.palette.grey[400] : theme.palette.grey[600], - fontSize: ".75rem" - } -})); diff --git a/deploy-web/src/components/shared/LinearLoadingSkeleton.tsx b/deploy-web/src/components/shared/LinearLoadingSkeleton.tsx index e7f19fbf1..a0abf7e67 100644 --- a/deploy-web/src/components/shared/LinearLoadingSkeleton.tsx +++ b/deploy-web/src/components/shared/LinearLoadingSkeleton.tsx @@ -1,40 +1,10 @@ "use client"; -import LinearProgress from "@mui/material/LinearProgress/LinearProgress"; -import { Progress } from "@src/components/ui/progress"; -import { useEffect, useState } from "react"; +import LinearProgress from "@mui/material/LinearProgress"; interface IProps { isLoading: boolean; } export function LinearLoadingSkeleton({ isLoading }: IProps) { - const [progress, setProgress] = useState(13); - - // useEffect(() => { - // let timer; - - // if (!isLoading) { - // setProgress(0); - // clearInterval(timer); - // } else { - // timer = setInterval(() => { - // setProgress(prev => { - // const rest = 100 - prev; - // const maxPercent = prev > 75 ? 10 : 35; - // const randomPercent = randomIntFromInterval(1, maxPercent); - // const increment = (randomPercent / 100) * rest; - // const newProgress = Math.min(prev + increment, 100); - // return newProgress; - // }); - // }, 500); - // } - - // return () => clearInterval(timer); - // }, [isLoading]); - - // function randomIntFromInterval(min: number, max: number) { - // return Math.floor(Math.random() * (max - min + 1) + min); - // } - return <>{isLoading ? <LinearProgress color="primary" /> : <div className="h-[4px] w-full min-w-0" />}</>; } diff --git a/deploy-web/src/components/shared/MustConnect.tsx b/deploy-web/src/components/shared/MustConnect.tsx index 8f0474c72..a23ae80f5 100644 --- a/deploy-web/src/components/shared/MustConnect.tsx +++ b/deploy-web/src/components/shared/MustConnect.tsx @@ -1,10 +1,8 @@ +"use client"; import React from "react"; import Link from "next/link"; import { UrlService } from "@src/utils/urlUtils"; -import { Alert } from "@mui/material"; -import { makeStyles } from "tss-react/mui"; - -export const useStyles = makeStyles()(theme => ({})); +import { Alert } from "../ui/alert"; export type Props = { message: string; @@ -12,7 +10,7 @@ export type Props = { export const MustConnect: React.FunctionComponent<Props> = ({ message }) => { return ( - <Alert severity="info" variant="outlined"> + <Alert> {message}, please{" "} <Link href={UrlService.login()} passHref> login diff --git a/deploy-web/src/components/shared/MustConnectModal.tsx b/deploy-web/src/components/shared/MustConnectModal.tsx index 9ba26e6bb..938208490 100644 --- a/deploy-web/src/components/shared/MustConnectModal.tsx +++ b/deploy-web/src/components/shared/MustConnectModal.tsx @@ -1,13 +1,8 @@ +"use client"; import React from "react"; -import Link from "next/link"; -import { UrlService } from "@src/utils/urlUtils"; -import { Alert } from "@mui/material"; -import { makeStyles } from "tss-react/mui"; import { Popup } from "./Popup"; import { MustConnect } from "./MustConnect"; -export const useStyles = makeStyles()(theme => ({})); - export type Props = { message: string; onClose: () => void; diff --git a/deploy-web/src/components/shared/PageContainer.tsx b/deploy-web/src/components/shared/PageContainer.tsx index 465cc2ab3..9459fbfe6 100644 --- a/deploy-web/src/components/shared/PageContainer.tsx +++ b/deploy-web/src/components/shared/PageContainer.tsx @@ -1,53 +1,12 @@ "use client"; import { cn } from "@src/utils/styleUtils"; import { ReactNode } from "react"; -import { LinearLoadingSkeleton } from "./LinearLoadingSkeleton"; -import { ErrorBoundary } from "react-error-boundary"; -import { ErrorFallback } from "./ErrorFallback"; -import { useWallet } from "@src/context/WalletProvider"; -import { useSettings } from "@src/context/SettingsProvider"; -import Spinner from "./Spinner"; type Props = { children?: ReactNode; className?: string; - isLoading?: boolean; - isUsingSettings?: boolean; - isUsingWallet?: boolean; }; -export const PageContainer: React.FunctionComponent<Props> = ({ children, className = "", isLoading, isUsingSettings, isUsingWallet }) => { - const { isSettingsInit } = useSettings(); - const { isWalletLoaded } = useWallet(); - - return ( - <div className={cn("overflow-auto", className)}> - {isLoading !== undefined && <LinearLoadingSkeleton isLoading={isLoading} />} - - <ErrorBoundary FallbackComponent={ErrorFallback}> - {!isUsingSettings || isSettingsInit ? ( - !isUsingWallet || isWalletLoaded ? ( - <div className="container h-full pb-8 pt-4 sm:pt-8">{children}</div> - ) : ( - <Loading text="Loading wallet..." /> - ) - ) : ( - <Loading text="Loading settings..." /> - )} - </ErrorBoundary> - </div> - ); -}; - -const Loading: React.FunctionComponent<{ text: string }> = ({ text }) => { - return ( - <div className="flex h-full w-full flex-col items-center justify-center pb-12 pt-12"> - <div className="pb-4"> - <Spinner size="large" /> - </div> - <div> - <h5>{text}</h5> - </div> - </div> - ); +export const PageContainer: React.FunctionComponent<Props> = ({ children, className = "" }) => { + return <div className={cn("container pb-8 pt-4 sm:pt-8", className)}>{children}</div>; }; diff --git a/deploy-web/src/components/shared/Pagninator.tsx b/deploy-web/src/components/shared/Pagninator.tsx index eff7d4b42..62677aa24 100644 --- a/deploy-web/src/components/shared/Pagninator.tsx +++ b/deploy-web/src/components/shared/Pagninator.tsx @@ -1,43 +1,30 @@ import { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious } from "../ui/pagination"; - type PaginatorProps = { currentPage: number; totalPages: number; onPageChange: (pageNumber: number) => void; showPreviousNext: boolean; -} - -export default function Paginator({ - currentPage, - totalPages, - onPageChange, - showPreviousNext, -}: PaginatorProps) { +}; +export default function Paginator({ currentPage, totalPages, onPageChange, showPreviousNext }: PaginatorProps) { return ( <Pagination> <PaginationContent> {showPreviousNext && totalPages ? ( <PaginationItem> - <PaginationPrevious - onClick={() => onPageChange(currentPage - 1)} - disabled={currentPage - 1 < 1} - /> + <PaginationPrevious onClick={() => onPageChange(currentPage - 1)} disabled={currentPage - 1 < 1} /> </PaginationItem> ) : null} {generatePaginationLinks(currentPage, totalPages, onPageChange)} {showPreviousNext && totalPages ? ( <PaginationItem> - <PaginationNext - onClick={() => onPageChange(currentPage + 1)} - disabled={currentPage > totalPages - 1} - /> + <PaginationNext onClick={() => onPageChange(currentPage + 1)} disabled={currentPage > totalPages - 1} /> </PaginationItem> - ): null} + ) : null} </PaginationContent> </Pagination> - ) + ); } const generatePaginationLinks = (currentPage: number, totalPages: number, onPageChange: (page: number) => void) => { @@ -63,7 +50,7 @@ const generatePaginationLinks = (currentPage: number, totalPages: number, onPage ); } if (2 < currentPage && currentPage < totalPages - 1) { - pages.push(<PaginationEllipsis />); + pages.push(<PaginationEllipsis key="ellipsis-1" />); pages.push( <PaginationItem key={currentPage}> <PaginationLink onClick={() => onPageChange(currentPage)} isActive={true}> @@ -72,7 +59,7 @@ const generatePaginationLinks = (currentPage: number, totalPages: number, onPage </PaginationItem> ); } - pages.push(<PaginationEllipsis />); + pages.push(<PaginationEllipsis key="ellipsis-2" />); for (let i = totalPages - 1; i <= totalPages; i++) { pages.push( <PaginationItem key={i}> diff --git a/deploy-web/src/components/shared/Popup.tsx b/deploy-web/src/components/shared/Popup.tsx index 86c919476..7d8e312cd 100644 --- a/deploy-web/src/components/shared/Popup.tsx +++ b/deploy-web/src/components/shared/Popup.tsx @@ -235,8 +235,8 @@ export function Popup(props: React.PropsWithChildren<PopupProps>) { )); component.push( <DialogFooter className="flex justify-between space-x-2 sm:justify-between" key="DialogCustomActions"> - <div>{leftButtons}</div> - <div>{rightButtons}</div> + <div className="space-x-2">{leftButtons}</div> + <div className="space-x-2">{rightButtons}</div> </DialogFooter> ); break; @@ -280,9 +280,9 @@ export function Popup(props: React.PropsWithChildren<PopupProps>) { // dividers={props.dividers} className={cn("m-0 p-4 ", { ["sm:max-w-[400px]"]: props.maxWidth === "xs", - ["sm:max-w-[425px]"]: props.maxWidth === "sm", - ["sm:max-w-[500px]"]: props.maxWidth === "md", - ["sm:max-w-[750px]"]: props.maxWidth === "lg", + ["sm:max-w-[600px]"]: props.maxWidth === "sm", + ["sm:max-w-[750px]"]: props.maxWidth === "md", + ["sm:max-w-[950px]"]: props.maxWidth === "lg", ["sm:max-w-[900px]"]: props.maxWidth === "xl" })} hideCloseButton={props.hideCloseButton} diff --git a/deploy-web/src/components/shared/PrerequisiteList.tsx b/deploy-web/src/components/shared/PrerequisiteList.tsx index be94d374d..a97ac4ff8 100644 --- a/deploy-web/src/components/shared/PrerequisiteList.tsx +++ b/deploy-web/src/components/shared/PrerequisiteList.tsx @@ -51,19 +51,20 @@ export const PrerequisiteList: React.FunctionComponent<Props> = ({ onClose, onCo label: "Close", color: "secondary", variant: "ghost", - side: "left", + side: "right", onClick: onClose }, { label: "Continue", color: "primary", variant: "default", + disabled: !isBalanceValidated, side: "right", isLoading: isLoadingPrerequisites, onClick: onContinue } ]} - onClose={onClose} + hideCloseButton maxWidth="sm" enableCloseOnBackdropClick={false} title="Checking Prerequisites" diff --git a/deploy-web/src/components/shared/SpecDetail.tsx b/deploy-web/src/components/shared/SpecDetail.tsx index 8881cbc07..724b503db 100644 --- a/deploy-web/src/components/shared/SpecDetail.tsx +++ b/deploy-web/src/components/shared/SpecDetail.tsx @@ -39,7 +39,9 @@ export function SpecDetail({ return ( <div - className={cn("grid grid-cols-1 sm:grid-cols-3", { + className={cn("grid ", { + ["grid-cols-1 sm:grid-cols-3"]: gpuAmount === 0, + ["grid-cols-1 sm:grid-cols-4"]: gpuAmount > 0, ["gap-1"]: gutterSize === "small", ["gap-2"]: gutterSize === "medium", ["gap-3"]: gutterSize === "large" diff --git a/deploy-web/src/components/shared/TemplateGridButton.tsx b/deploy-web/src/components/shared/TemplateGridButton.tsx index adff2eada..6f1274187 100644 --- a/deploy-web/src/components/shared/TemplateGridButton.tsx +++ b/deploy-web/src/components/shared/TemplateGridButton.tsx @@ -1,40 +1,31 @@ -import { Grid, Paper, Typography } from "@mui/material"; +"use client"; import { getShortText } from "@src/hooks/useShortText"; import { ITemplate } from "@src/types"; import { UrlService } from "@src/utils/urlUtils"; import Link from "next/link"; -import { makeStyles } from "tss-react/mui"; +import { CardContent, CardHeader, cardClasses } from "../ui/card"; +import { cn } from "@src/utils/styleUtils"; type Props = { template: Partial<ITemplate>; onClick?: () => void; }; -const useStyles = makeStyles()(theme => ({ - templateButton: { - height: "100%", - cursor: "pointer", - padding: "1rem", - transition: "background-color .3s ease", - "&:hover": { - backgroundColor: theme.palette.mode === "dark" ? theme.palette.grey[900] : theme.palette.grey[100] - } - } -})); - export const TemplateGridButton: React.FunctionComponent<Props> = ({ template, onClick }) => { - const { classes } = useStyles(); - return ( - <Grid item xs={12} sm={6} lg={3}> - <Link href={UrlService.template(template.id)}> - <Paper className={classes.templateButton} onClick={onClick}> - <Typography variant="body1" sx={{ fontWeight: "bold" }}> - {template.title} - </Typography> - <Typography variant="caption">{getShortText(template.description || "", 50)}</Typography> - </Paper> - </Link> - </Grid> + <Link + className={cn(cardClasses, "min-h-[100px] cursor-pointer !no-underline hover:bg-primary/10")} + href={UrlService.template(template.id as string)} + onClick={onClick} + > + <CardHeader> + <div className="flex items-center"> + <div className="break-all font-bold">{template.title}</div> + </div> + </CardHeader> + <CardContent className="pb-4 pt-0"> + <p className="text-sm text-muted-foreground">{getShortText(template.description || "", 50)}</p> + </CardContent> + </Link> ); }; diff --git a/deploy-web/src/components/shared/UserFavoriteButton.tsx b/deploy-web/src/components/shared/UserFavoriteButton.tsx index de9c16679..04dae7286 100644 --- a/deploy-web/src/components/shared/UserFavoriteButton.tsx +++ b/deploy-web/src/components/shared/UserFavoriteButton.tsx @@ -1,12 +1,13 @@ +"use client"; import React, { ReactNode, useState } from "react"; -import { CircularProgress, IconButton } from "@mui/material"; -import StarIcon from "@mui/icons-material/Star"; -import StarOutlineIcon from "@mui/icons-material/StarOutline"; import { useAddFavoriteTemplate, useRemoveFavoriteTemplate } from "@src/queries/useTemplateQuery"; -import { Snackbar } from "./Snackbar"; -import { useSnackbar } from "notistack"; import { useCustomUser } from "@src/hooks/useCustomUser"; import { MustConnectModal } from "./MustConnectModal"; +import { Button } from "../ui/button"; +import Spinner from "./Spinner"; +import { MdStar, MdStarOutline } from "react-icons/md"; +import { useToast } from "../ui/use-toast"; +import { cn } from "@src/utils/styleUtils"; type Props = { id: string; @@ -23,7 +24,7 @@ export const UserFavoriteButton: React.FunctionComponent<Props> = ({ id, isFavor const { mutate: removeFavorite, isLoading: isRemoving } = useRemoveFavoriteTemplate(id); const [showMustConnectModal, setShowMustConnectModal] = useState(false); const isSaving = isAdding || isRemoving; - const { enqueueSnackbar } = useSnackbar(); + const { toast } = useToast(); const onFavoriteClick = async () => { try { @@ -44,18 +45,16 @@ export const UserFavoriteButton: React.FunctionComponent<Props> = ({ id, isFavor setIsFavorite(prev => !prev); } catch (error) { console.log(error); - enqueueSnackbar(<Snackbar title="An error has occured." iconVariant="error" />, { - variant: "error" - }); + toast({ title: "An error has occured.", variant: "destructive" }); } }; return ( <> {showMustConnectModal && <MustConnectModal message="To add template favorites" onClose={() => setShowMustConnectModal(false)} />} - <IconButton size="small" onClick={onFavoriteClick} color={isFavorite ? "secondary" : "default"}> - {isSaving ? <CircularProgress size="1.5rem" color="secondary" /> : isFavorite ? <StarIcon /> : <StarOutlineIcon />} - </IconButton> + <Button size="icon" onClick={onFavoriteClick} variant="ghost" className={cn({ ["text-primary"]: isFavorite }, "text-xl")}> + {isSaving ? <Spinner size="small" /> : isFavorite ? <MdStar /> : <MdStarOutline />} + </Button> </> ); }; diff --git a/deploy-web/src/app/templates/MobileTemplatesFilter.tsx b/deploy-web/src/components/templates/MobileTemplatesFilter.tsx similarity index 100% rename from deploy-web/src/app/templates/MobileTemplatesFilter.tsx rename to deploy-web/src/components/templates/MobileTemplatesFilter.tsx diff --git a/deploy-web/src/app/templates/TemplateBox.tsx b/deploy-web/src/components/templates/TemplateBox.tsx similarity index 100% rename from deploy-web/src/app/templates/TemplateBox.tsx rename to deploy-web/src/components/templates/TemplateBox.tsx diff --git a/deploy-web/src/components/templates/TemplateDetail.tsx b/deploy-web/src/components/templates/TemplateDetail.tsx new file mode 100644 index 000000000..3cc9435e9 --- /dev/null +++ b/deploy-web/src/components/templates/TemplateDetail.tsx @@ -0,0 +1,123 @@ +"use client"; +import { useState } from "react"; +import { useRouter } from "next/navigation"; +import Link from "next/link"; +import { UrlService } from "@src/utils/urlUtils"; +import ViewPanel from "@src/components/shared/ViewPanel"; +import { DynamicMonacoEditor } from "@src/components/shared/DynamicMonacoEditor"; +import { LinearLoadingSkeleton } from "@src/components/shared/LinearLoadingSkeleton"; +import { PageContainer } from "@src/components/shared/PageContainer"; +import { RouteStepKeys } from "@src/utils/constants"; +import { ApiTemplate } from "@src/types"; +import Markdown from "@src/components/shared/Markdown"; +import { usePreviousRoute } from "@src/hooks/usePreviousRoute"; +import { useTemplates } from "@src/context/TemplatesProvider"; +import { Tabs, TabsList, TabsTrigger } from "@src/components/ui/tabs"; +import { NavArrowLeft, Rocket } from "iconoir-react"; +import { Button, buttonVariants } from "@src/components/ui/button"; +import { cn } from "@src/utils/styleUtils"; +import GitHubIcon from "@mui/icons-material/GitHub"; +import { CustomNextSeo } from "../shared/CustomNextSeo"; +import { getShortText } from "@src/hooks/useShortText"; +import Layout from "../layout/Layout"; + +type Props = { + templateId: string; + template: ApiTemplate; +}; + +export const TemplateDetail: React.FunctionComponent<Props> = ({ templateId, template }) => { + const [activeTab, setActiveTab] = useState("README"); + const { getTemplateById, isLoading } = useTemplates(); + const router = useRouter(); + const _template = template || getTemplateById(templateId); + const previousRoute = usePreviousRoute(); + + function handleBackClick() { + if (previousRoute) { + router.back(); + } else { + router.push(UrlService.templates()); + } + } + + function handleOpenGithub() { + window.open(_template.githubUrl, "_blank"); + } + + return ( + <Layout isLoading={isLoading} disableContainer> + <div className="[&>img]:max-w-full"> + <CustomNextSeo + title={`Template detail${_template ? " " + _template?.name : ""}`} + url={`https://deploy.cloudmos.io${UrlService.templateDetails(templateId)}`} + description={getShortText(_template.summary || "", 140)} + /> + + <Tabs value={activeTab} onValueChange={setActiveTab}> + <TabsList className="w-full"> + <TabsTrigger value="README" className={cn({ ["font-bold"]: activeTab === "README" })}> + README + </TabsTrigger> + <TabsTrigger value="SDL" className={cn({ ["font-bold"]: activeTab === "SDL" })}> + View SDL + </TabsTrigger> + {_template?.guide && ( + <TabsTrigger value="GUIDE" className={cn({ ["font-bold"]: activeTab === "GUIDE" })}> + Guide + </TabsTrigger> + )} + </TabsList> + <LinearLoadingSkeleton isLoading={isLoading} /> + + <div className="container flex h-full px-4 py-2 sm:pt-8"> + <div className="flex items-center truncate"> + <Button aria-label="back" onClick={handleBackClick} size="icon" variant="ghost"> + <NavArrowLeft /> + </Button> + <div className="text-truncate"> + <h3 className="ml-4 text-xl font-bold sm:text-2xl md:text-3xl">{_template?.name}</h3> + </div> + + <div className="ml-4"> + <Button aria-label="View on github" title="View on Github" onClick={handleOpenGithub} size="icon" variant="ghost"> + <GitHubIcon fontSize="medium" /> + </Button> + </div> + + <Link + className={cn(buttonVariants({ variant: "default" }), "ml-4 md:ml-8")} + href={UrlService.newDeployment({ step: RouteStepKeys.editDeployment, templateId: _template?.id })} + > + Deploy  + <Rocket className="rotate-45" /> + </Link> + </div> + </div> + + {activeTab === "README" && ( + <ViewPanel stickToBottom className="overflow-auto pb-12"> + <PageContainer> + <Markdown>{_template?.readme}</Markdown> + </PageContainer> + </ViewPanel> + )} + {activeTab === "SDL" && ( + <ViewPanel stickToBottom className="overflow-hidden"> + <PageContainer className="h-full"> + <DynamicMonacoEditor height="100%" language="yaml" value={_template?.deploy || ""} options={{ readOnly: true }} /> + </PageContainer> + </ViewPanel> + )} + {activeTab === "GUIDE" && ( + <ViewPanel stickToBottom className="overflow-auto p-4 pb-12"> + <PageContainer> + <Markdown>{_template?.guide}</Markdown> + </PageContainer> + </ViewPanel> + )} + </Tabs> + </div> + </Layout> + ); +}; diff --git a/deploy-web/src/app/templates/TemplateGallery.tsx b/deploy-web/src/components/templates/TemplateGallery.tsx similarity index 93% rename from deploy-web/src/app/templates/TemplateGallery.tsx rename to deploy-web/src/components/templates/TemplateGallery.tsx index 8cd7aa5ad..5466a34dc 100644 --- a/deploy-web/src/app/templates/TemplateGallery.tsx +++ b/deploy-web/src/components/templates/TemplateGallery.tsx @@ -3,10 +3,7 @@ import { useState, useEffect } from "react"; import { useTemplates } from "../../context/TemplatesProvider"; import { useRouter, useSearchParams } from "next/navigation"; import { UrlService } from "@src/utils/urlUtils"; -import { PageContainer } from "@src/components/shared/PageContainer"; -import { TemplateBox } from "@src/app/templates/TemplateBox"; import { LinkTo } from "@src/components/shared/LinkTo"; -import { MobileTemplatesFilter } from "@src/app/templates/MobileTemplatesFilter"; import { ApiTemplate } from "@src/types"; import { Button, buttonVariants } from "@src/components/ui/button"; import { FilterList, Xmark } from "iconoir-react"; @@ -15,6 +12,10 @@ import Spinner from "@src/components/shared/Spinner"; import { cn } from "@src/utils/styleUtils"; import TextField from "@mui/material/TextField"; import IconButton from "@mui/material/IconButton"; +import { CustomNextSeo } from "../shared/CustomNextSeo"; +import Layout from "../layout/Layout"; +import { TemplateBox } from "./TemplateBox"; +import { MobileTemplatesFilter } from "./MobileTemplatesFilter"; type Props = {}; @@ -116,7 +117,13 @@ export const TemplateGallery: React.FunctionComponent<Props> = ({}) => { ); return ( - <PageContainer isLoading={isLoadingTemplates}> + <Layout isLoading={isLoadingTemplates}> + <CustomNextSeo + title="Template Gallery" + url={`https://deploy.cloudmos.io${UrlService.templates()}`} + description="Explore all the templates made by the community to easily deploy any docker container on the Akash Network." + /> + <div className="mb-6 text-center sm:mb-8 md:mb-12"> <h1 className="mb-4 text-2xl font-bold sm:text-3xl md:text-4xl">Find your Template</h1> @@ -215,6 +222,6 @@ export const TemplateGallery: React.FunctionComponent<Props> = ({}) => { )} </div> </div> - </PageContainer> + </Layout> ); }; diff --git a/deploy-web/src/components/templates/UserTemplate.tsx b/deploy-web/src/components/templates/UserTemplate.tsx new file mode 100644 index 000000000..ed611954a --- /dev/null +++ b/deploy-web/src/components/templates/UserTemplate.tsx @@ -0,0 +1,213 @@ +"use client"; +import { Title } from "@src/components/shared/Title"; +import { PageContainer } from "@src/components/shared/PageContainer"; +import { RouteStepKeys } from "@src/utils/constants"; +import { ITemplate } from "@src/types"; +import { UrlService } from "@src/utils/urlUtils"; +import Link from "next/link"; +import { LeaseSpecDetail } from "@src/components/shared/LeaseSpecDetail"; +import { bytesToShrink } from "@src/utils/unitUtils"; +import { roundDecimal } from "@src/utils/mathHelpers"; +import { useEffect, useState } from "react"; +import { useCustomUser } from "@src/hooks/useCustomUser"; +import { Popup } from "@src/components/shared/Popup"; +import { useDeleteTemplate } from "@src/queries/useTemplateQuery"; +import { useRouter } from "next/navigation"; +import { EditDescriptionForm } from "@src/components/sdl/EditDescriptionForm"; +import { event } from "nextjs-google-analytics"; +import { AnalyticsEvents } from "@src/utils/analytics"; +import { useAtom } from "jotai"; +import sdlStore from "@src/store/sdlStore"; +import { UserFavoriteButton } from "@src/components/shared/UserFavoriteButton"; +import { Card, CardContent } from "@src/components/ui/card"; +import { Button, buttonVariants } from "@src/components/ui/button"; +import { Bin, Edit, Rocket } from "iconoir-react"; +import { cn } from "@src/utils/styleUtils"; +import { CustomNextSeo } from "../shared/CustomNextSeo"; +import { getShortText } from "@src/hooks/useShortText"; +import Layout from "../layout/Layout"; + +type Props = { + id: string; + template: ITemplate; +}; + +export const UserTemplate: React.FunctionComponent<Props> = ({ id, template }) => { + const [description, setDescription] = useState(""); + const [isShowingDelete, setIsShowingDelete] = useState(false); + const [isEditingDescription, setIsEditingDescription] = useState(false); + const { user } = useCustomUser(); + const { mutate: deleteTemplate, isLoading: isDeleting } = useDeleteTemplate(id); + const isCurrentUserTemplate = user?.sub === template.userId; + const _ram = bytesToShrink(template.ram); + const _storage = bytesToShrink(template.storage); + const router = useRouter(); + const [, setDeploySdl] = useAtom(sdlStore.deploySdl); + + useEffect(() => { + const desc = template.description || ""; + setDescription(desc); + }, []); + + const onDeleteTemplate = async () => { + await deleteTemplate(); + + event(AnalyticsEvents.DEPLOY_SDL, { + category: "sdl_builder", + label: "Delete SDL template from detail" + }); + + router.replace(UrlService.userProfile(template.username)); + }; + + const onDeleteClose = () => { + setIsShowingDelete(false); + }; + + const onDescriptionSave = (desc: string) => { + setDescription(desc); + setIsEditingDescription(false); + + event(AnalyticsEvents.SAVE_SDL_DESCRIPTION, { + category: "sdl_builder", + label: "Save SDL description" + }); + }; + return ( + <Layout> + <CustomNextSeo + title={`${template.title}`} + url={`https://deploy.cloudmos.io${UrlService.template(id)}`} + description={getShortText(template.description || "", 140)} + /> + + <Popup + fullWidth + variant="custom" + actions={[ + { + label: "Close", + color: "primary", + variant: "text", + side: "left", + onClick: onDeleteClose + }, + { + label: "Confirm", + color: "secondary", + variant: "default", + side: "right", + isLoading: isDeleting, + onClick: onDeleteTemplate + } + ]} + onClose={onDeleteClose} + maxWidth="xs" + enableCloseOnBackdropClick + open={isShowingDelete} + title="Delete template" + > + Are you sure you want to delete template: "{template.title}"? + </Popup> + + <div className="mb-4 flex items-baseline"> + <Title className="m-0">{template.title} +   by  + { + event(AnalyticsEvents.CLICK_SDL_PROFILE, { + category: "sdl_builder", + label: "Click on SDL user profile in template detail" + }); + }} + > + {template.username} + +
+ +
+ + + { + event(AnalyticsEvents.CLICK_EDIT_SDL_TEMPLATE, { + category: "sdl_builder", + label: "Click on edit SDL template" + }); + }} + > + Edit + + + { + event(AnalyticsEvents.ADD_SDL_FAVORITE, { + category: "sdl_builder", + label: "Add SDL to favorites" + }); + }} + onRemoveFavorite={() => { + event(AnalyticsEvents.REMOVE_SDL_FAVORITE, { + category: "sdl_builder", + label: "Remove SDL from favorites" + }); + }} + /> + + {isCurrentUserTemplate && ( + + )} +
+ +
+ + + +
+ + {isEditingDescription ? ( + setIsEditingDescription(false)} onSave={onDescriptionSave} /> + ) : ( + + + {isCurrentUserTemplate && ( +
+ +
+ )} + + {description ? description :

No description...

} +
+
+ )} + + ); +}; diff --git a/deploy-web/src/components/ui/input.tsx b/deploy-web/src/components/ui/input.tsx index 47c4e8bc9..6c77187ef 100644 --- a/deploy-web/src/components/ui/input.tsx +++ b/deploy-web/src/components/ui/input.tsx @@ -50,7 +50,7 @@ const FormInput = React.forwardRef(({ classNam FormInput.displayName = "FormInput"; export interface InputWithIconProps extends React.InputHTMLAttributes { - label: string | React.ReactNode; + label?: string | React.ReactNode; startIcon?: React.ReactNode; endIcon?: React.ReactNode; error?: string; @@ -65,7 +65,7 @@ const InputWithIcon = React.forwardRef( return (
- + {label && }
{startIcon &&
{startIcon}
} = ({}) => { + const { addressNames, editAddressName, isLoading: isLoadingAddressBook } = useAddressBook(); + const { user, isLoading } = useCustomUser(); + + const addressNamesArray = Object.keys(addressNames || {}).map(address => ({ address, name: addressNames[address] })); + + return ( + + + + + + {(isLoading || isLoadingAddressBook) && ( +
+ +
+ )} + + {!isLoading && addressNamesArray?.length === 0 && ( +
+

No saved addresses.

+
+ )} + + {addressNamesArray.length > 0 && ( + + + + Address + Name + + + + + + {addressNamesArray.map(({ address, name }) => ( + + + + + {name} + + + + + ))} + +
+ )} + + {!isLoading && !isLoadingAddressBook && ( + + )} +
+
+
+
+ ); +}; diff --git a/deploy-web/src/components/user/UserFavorites.tsx b/deploy-web/src/components/user/UserFavorites.tsx new file mode 100644 index 000000000..3b05b817c --- /dev/null +++ b/deploy-web/src/components/user/UserFavorites.tsx @@ -0,0 +1,99 @@ +import { useUserFavoriteTemplates } from "@src/queries/useTemplateQuery"; +import { IUserSetting } from "@src/types/user"; +import { TemplateGridButton } from "@src/components/shared/TemplateGridButton"; +import { useCustomUser } from "@src/hooks/useCustomUser"; +import { event } from "nextjs-google-analytics"; +import { AnalyticsEvents } from "@src/utils/analytics"; +import { UserProfileLayout } from "@src/components/user/UserProfileLayout"; +import Spinner from "@src/components/shared/Spinner"; +import { NextSeo } from "next-seo"; +import Layout from "../layout/Layout"; + +type Props = {}; + +export const UserFavorites: React.FunctionComponent = () => { + const { data: favoriteTemplates, isLoading: isLoadingTemplates } = useUserFavoriteTemplates(); + const { user, isLoading } = useCustomUser(); + + return ( + + + + {isLoadingTemplates && ( +
+ +
+ )} + +
+ {!isLoadingTemplates && favoriteTemplates?.length === 0 && ( +
+

No template favorites.

+
+ )} + + {favoriteTemplates?.map(t => ( + { + event(AnalyticsEvents.USER_PROFILE_CLICK_TEMPLATE, { + category: "profile", + label: "Click on template from templates" + }); + }} + /> + ))} +
+
+
+ ); +}; + +// type Props = {}; + +// export default const UserFavoritesPage: NextPage = withCustomPageAuthRequired(({}) => { +// const { data: favoriteTemplates, isLoading: isLoadingTemplates } = useUserFavoriteTemplates(); +// const { user, isLoading } = useCustomUser(); + +// return ( +// +// + +// +// {(isLoading || isLoadingTemplates) && } + +// +// {!isLoadingTemplates && favoriteTemplates?.length === 0 && ( +// +// No template favorites. +// +// )} + +// {favoriteTemplates?.map(t => ( +// { +// event(AnalyticsEvents.USER_PROFILE_CLICK_TEMPLATE, { +// category: "settings", +// label: "Click on template from template favorites" +// }); +// }} +// /> +// ))} +// +// +// +// ); +// }; + +// export default UserFavoritesPage; + +// export const getServerSideProps = withCustomPageAuthRequired({ +// async getServerSideProps({ params, req, res }) { +// return { +// props: {} +// }; +// } +// }); diff --git a/deploy-web/src/components/user/UserProfile.tsx b/deploy-web/src/components/user/UserProfile.tsx new file mode 100644 index 000000000..fbeb5f177 --- /dev/null +++ b/deploy-web/src/components/user/UserProfile.tsx @@ -0,0 +1,72 @@ +"use client"; +import { UrlService } from "@src/utils/urlUtils"; +import Link from "next/link"; +import { useUserTemplates } from "@src/queries/useTemplateQuery"; +import { IUserSetting } from "@src/types/user"; +import { TemplateGridButton } from "@src/components/shared/TemplateGridButton"; +import { useCustomUser } from "@src/hooks/useCustomUser"; +import { event } from "nextjs-google-analytics"; +import { AnalyticsEvents } from "@src/utils/analytics"; +import { UserProfileLayout } from "@src/components/user/UserProfileLayout"; +import Spinner from "@src/components/shared/Spinner"; +import { buttonVariants } from "@src/components/ui/button"; +import { cn } from "@src/utils/styleUtils"; +import Layout from "../layout/Layout"; + +type Props = { + username: string; + user: IUserSetting; +}; + +export const UserProfile: React.FunctionComponent = ({ username, user }) => { + const { data: userTemplates, isLoading: isLoadingTemplates } = useUserTemplates(username); + const { user: _user, isLoading } = useCustomUser(); + + return ( + + + {isLoadingTemplates && ( +
+ +
+ )} + +
+ {!isLoadingTemplates && userTemplates?.length === 0 && ( +
+

No public templates.

+ + {username === _user?.username && ( + { + event(AnalyticsEvents.CREATE_SDL_TEMPLATE_LINK, { + category: "profile", + label: "Create SDL template link from profile" + }); + }} + > + Create one! + + )} +
+ )} + + {userTemplates?.map(t => ( + { + event(AnalyticsEvents.USER_PROFILE_CLICK_TEMPLATE, { + category: "profile", + label: "Click on template from templates" + }); + }} + /> + ))} +
+
+
+ ); +}; diff --git a/deploy-web/src/components/user/UserProfileLayout.tsx b/deploy-web/src/components/user/UserProfileLayout.tsx index 1592be021..cae62b07b 100644 --- a/deploy-web/src/components/user/UserProfileLayout.tsx +++ b/deploy-web/src/components/user/UserProfileLayout.tsx @@ -1,13 +1,13 @@ import React, { ReactNode } from "react"; -import PageContainer from "../shared/PageContainer"; -import { Box, Tab, Tabs, Typography } from "@mui/material"; -import { makeStyles } from "tss-react/mui"; -import { a11yTabProps } from "@src/utils/a11y"; import { useRouter } from "next/router"; import { UrlService } from "@src/utils/urlUtils"; import { event } from "nextjs-google-analytics"; import { AnalyticsEvents } from "@src/utils/analytics"; import { useCustomUser } from "@src/hooks/useCustomUser"; +import { Tabs, TabsList, TabsTrigger } from "@src/components/ui/tabs"; +import { ErrorBoundary } from "react-error-boundary"; +import { ErrorFallback } from "@src/components/shared/ErrorFallback"; +import { PageContainer } from "@src/components/shared/PageContainer"; type UserProfileTab = "templates" | "favorites" | "address-book" | "settings"; type Props = { @@ -17,26 +17,16 @@ type Props = { page: UserProfileTab; }; -const useStyles = makeStyles()(theme => ({ - title: { - fontSize: "2rem", - fontWeight: "bold", - marginBottom: "1rem" - }, - titleSmall: { - fontSize: "1.1rem" - }, - selectedTab: { - fontWeight: "bold" - } -})); - export const UserProfileLayout: React.FunctionComponent = ({ page, children, username, bio }) => { - const { classes } = useStyles(); const router = useRouter(); const { user } = useCustomUser(); - const handleTabChange = (event: React.SyntheticEvent, newValue: string) => { + const handleTabChange = (newValue: string) => { + event(AnalyticsEvents.USER_PROFILE_TEMPLATE_TAB, { + category: "profile", + label: `Click on ${newValue} tab` + }); + switch (newValue) { case "templates": router.push(UrlService.userProfile(username)); @@ -54,88 +44,27 @@ export const UserProfileLayout: React.FunctionComponent = ({ page, childr }; return ( - - - - {username} - - - {bio && ( - - {bio} - - )} - + <> +
+

{username}

- - - { - event(AnalyticsEvents.USER_PROFILE_TEMPLATE_TAB, { - category: "profile", - label: "Click on templates tab" - }); - }} - /> + {bio &&

{bio}

} +
- {/** Only show favorites/address book/settings for current user */} - {user?.username === username && [ - { - event(AnalyticsEvents.USER_PROFILE_FAVORITES_TAB, { - category: "profile", - label: "Click on favorites tab" - }); - }} - />, - { - event(AnalyticsEvents.USER_PROFILE_ADDRESS_BOOK_TAB, { - category: "profile", - label: "Click on address book tab" - }); - }} - />, - { - event(AnalyticsEvents.USER_PROFILE_SETTINGS_TAB, { - category: "profile", - label: "Click on settings tab" - }); - }} - /> - ]} - - + + + Templates + {user?.username === username && ( + <> + Favorites + Address Book + Settings + + )} + - {children} -
+ {children} + + ); }; diff --git a/deploy-web/src/components/user/UserSettingsForm.tsx b/deploy-web/src/components/user/UserSettingsForm.tsx new file mode 100644 index 000000000..7b5fa82eb --- /dev/null +++ b/deploy-web/src/components/user/UserSettingsForm.tsx @@ -0,0 +1,241 @@ +import { NextSeo } from "next-seo"; +import { LabelValue } from "@src/components/shared/LabelValue"; +import { useEffect, useState } from "react"; +import axios from "axios"; +import { useSaveSettings } from "@src/queries/useSettings"; +import { useCustomUser } from "@src/hooks/useCustomUser"; +import { UserSettings } from "@src/types/user"; +import { Controller, useForm } from "react-hook-form"; +import { event } from "nextjs-google-analytics"; +import { AnalyticsEvents } from "@src/utils/analytics"; +import { UserProfileLayout } from "@src/components/user/UserProfileLayout"; +import Spinner from "@src/components/shared/Spinner"; +import { FormPaper } from "@src/components/sdl/FormPaper"; +import { Button } from "@src/components/ui/button"; +import { Alert } from "@src/components/ui/alert"; +import { CheckCircle } from "iconoir-react"; +import { MdHighlightOff } from "react-icons/md"; +import { Switch } from "@src/components/ui/switch"; +import { Input, InputWithIcon, Textarea } from "@src/components/ui/input"; +import Layout from "../layout/Layout"; + +type Props = {}; + +export const UserSettingsForm: React.FunctionComponent = ({}) => { + const { user, isLoading } = useCustomUser(); + const [isCheckingAvailability, setIsCheckingAvailability] = useState(false); + const [isAvailable, setIsAvailable] = useState(null); + const { + getValues, + register, + handleSubmit, + setValue, + control, + watch, + formState: { isDirty, errors } + } = useForm({ + defaultValues: { + username: "", + subscribedToNewsletter: false, + bio: "", + youtubeUsername: "", + twitterUsername: "", + githubUsername: "" + } + }); + const { mutate: saveSettings, isLoading: isSaving } = useSaveSettings(); + const { username } = watch(); + + const isFormDisabled = isLoading || isSaving; + const canSave = !isFormDisabled && isDirty && isAvailable !== false; + + useEffect(() => { + if (user) { + setValue("username", user.username); + setValue("subscribedToNewsletter", user.subscribedToNewsletter); + setValue("bio", user.bio); + setValue("youtubeUsername", user.youtubeUsername); + setValue("twitterUsername", user.twitterUsername); + setValue("githubUsername", user.githubUsername); + } + }, [user?.username, user?.subscribedToNewsletter, user?.bio, user?.youtubeUsername, user?.twitterUsername, user?.githubUsername]); + + useEffect(() => { + if (user && username && username.length >= 3 && username.length <= 40 && username !== user.username) { + const timeoutId = setTimeout(async () => { + setIsCheckingAvailability(true); + const response = await axios.get("/api/proxy/user/checkUsernameAvailability/" + username); + + setIsCheckingAvailability(false); + setIsAvailable(response.data.isAvailable); + }, 500); + + return () => clearTimeout(timeoutId); + } else { + setIsAvailable(null); + } + }, [user?.username, username]); + + async function onSubmit() { + saveSettings(getValues()); + + event(AnalyticsEvents.USER_SETTINGS_SAVE, { + category: "settings", + label: "Save user settings" + }); + } + + // function onUpgradeClick(ev) { + // ev.preventDefault(); + + // event(AnalyticsEvents.USER_SETTINGS_UPGRADE_PLAN, { + // category: "settings", + // label: "Click on upgrade plan from user settings" + // }); + + // router.push(UrlService.pricing()); + // } + + return ( + + + + {isLoading || !user ? ( +
+ +
+ ) : ( + +
+ + +
+ + {isCheckingAvailability && } + + {!isCheckingAvailability && isAvailable && ( + <> + +  Username is available + + )} + {!isCheckingAvailability && isAvailable === false && ( + <> + +  Username is not available + + )} + +
+ {errors.username && ( + + {errors.username.message} + + )} + + } + /> + + } + /> +
+ } + /> + {/* + {user.plan.name} + {user.planCode === "COMMUNITY" ? ( + + ) : ( + + + + )} + + } + /> */} + } /> + + https://www.youtube.com/c/
} + // startAdornment={https://www.youtube.com/c/} + /> + } + /> + https://x.com/
} + // startAdornment={https://twitter.com/} + /> + } + /> + https://github.com/
} + // startAdornment={https://github.com/} + /> + } + /> + + + + + )} + + + ); +}; diff --git a/deploy-web/src/context/AddressBookProvider/AddressBookProvider.tsx b/deploy-web/src/context/AddressBookProvider/AddressBookProvider.tsx index eb2735f0b..e47d8f312 100644 --- a/deploy-web/src/context/AddressBookProvider/AddressBookProvider.tsx +++ b/deploy-web/src/context/AddressBookProvider/AddressBookProvider.tsx @@ -21,7 +21,7 @@ export const AddressBookProvider = ({ children }) => { return ( - {!isLoading && !!editingAddress && ( + {!isLoading && editingAddress !== null && ( setEditingAddress(null)} /> )} {children} diff --git a/deploy-web/src/context/AddressBookProvider/EditAddressBookmarkModal.tsx b/deploy-web/src/context/AddressBookProvider/EditAddressBookmarkModal.tsx index c18d186d8..66d2fef74 100644 --- a/deploy-web/src/context/AddressBookProvider/EditAddressBookmarkModal.tsx +++ b/deploy-web/src/context/AddressBookProvider/EditAddressBookmarkModal.tsx @@ -1,14 +1,14 @@ +"use client"; import { ReactNode, useEffect, useState } from "react"; -import { Popup } from "../../components/shared/Popup"; -import { Box, Paper, TextField } from "@mui/material"; import { getSplitText } from "@src/hooks/useShortText"; -import CheckIcon from "@mui/icons-material/Check"; import { useRemoveAddressName, useSaveAddressName } from "@src/queries/useAddressNames"; -import DeleteIcon from "@mui/icons-material/Delete"; -import { useSnackbar } from "notistack"; -import { Snackbar } from "../../components/shared/Snackbar"; import { event } from "nextjs-google-analytics"; import { AnalyticsEvents } from "@src/utils/analytics"; +import { useToast } from "@src/components/ui/use-toast"; +import { Popup } from "@src/components/shared/Popup"; +import { Bin, Check } from "iconoir-react"; +import { InputWithIcon } from "@src/components/ui/input"; +import { FormPaper } from "@src/components/sdl/FormPaper"; type Props = { open: boolean; @@ -20,22 +20,21 @@ type Props = { export const EditAddressBookmarkModal: React.FunctionComponent = ({ open, address, addressNames, onClose }) => { const [customName, setCustomName] = useState(""); - const { mutate: saveAddress, isLoading: isSaving } = useSaveAddressName(address); - const { mutate: deleteAddress, isLoading: isDeleting } = useRemoveAddressName(address); - const { enqueueSnackbar } = useSnackbar(); + const [_address, setAddress] = useState(address); + const { mutate: saveAddress, isLoading: isSaving } = useSaveAddressName(_address); + const { mutate: deleteAddress, isLoading: isDeleting } = useRemoveAddressName(_address); + const { toast } = useToast(); useEffect(() => { if (open) { - setCustomName(addressNames[address] || ""); + setCustomName(addressNames[_address] || ""); } }, [open]); async function onSaveClick() { await saveAddress(customName); - enqueueSnackbar(, { - variant: "success" - }); + toast({ title: "Address saved!", variant: "success" }); event(AnalyticsEvents.ADDRESS_BOOK_SAVE_ADDRESS, { category: "settings", @@ -65,19 +64,18 @@ export const EditAddressBookmarkModal: React.FunctionComponent = ({ open, fullWidth open={open} variant="custom" - title={addressNames[address] ? <>Edit {getSplitText(address)} name : <>Add {getSplitText(address)} to Address Book} + title={addressNames[_address] ? <>Edit {getSplitText(_address)} name : <>Add {getSplitText(_address)} to Address Book} actions={[ { label: ( <> - Remove  - + Remove + ), - color: "secondary", - variant: "contained", + variant: "ghost", side: "left", - disabled: isSaving || isDeleting || !(address in addressNames), + disabled: isSaving || isDeleting || !(_address in addressNames), onClick: onDeleteClick }, { @@ -92,11 +90,10 @@ export const EditAddressBookmarkModal: React.FunctionComponent = ({ open, label: ( <> Save  - + ), - color: "secondary", - variant: "contained", + variant: "default", side: "right", disabled: !customName || isSaving || isDeleting, onClick: onSaveClick @@ -106,17 +103,30 @@ export const EditAddressBookmarkModal: React.FunctionComponent = ({ open, maxWidth="sm" enableCloseOnBackdropClick={!isSaving && !isDeleting} > - - - +
+ setAddress(ev.target.value)} + // Disabled if saving, deleting or if there is an address + disabled={isSaving || isDeleting || !!address} + className="w-full" + /> +
+ +
+ setCustomName(ev.target.value)} disabled={isSaving || isDeleting} - fullWidth + className="w-full" /> - - +
+ ); }; diff --git a/deploy-web/src/context/BackgroundTaskProvider/BackgroundTaskProvider.tsx b/deploy-web/src/context/BackgroundTaskProvider/BackgroundTaskProvider.tsx index 1c0466359..408eb9501 100644 --- a/deploy-web/src/context/BackgroundTaskProvider/BackgroundTaskProvider.tsx +++ b/deploy-web/src/context/BackgroundTaskProvider/BackgroundTaskProvider.tsx @@ -1,11 +1,10 @@ "use client"; -import { Button } from "@mui/material"; -import { Snackbar } from "@src/components/shared/Snackbar"; import { PROVIDER_PROXY_URL_WS } from "@src/utils/constants"; -import { useSnackbar } from "notistack"; import React from "react"; import { useCertificate } from "../CertificateProvider"; import FileSaver from "file-saver"; +import { useToast } from "@src/components/ui/use-toast"; +import { Button } from "@src/components/ui/button"; const getPrintCommand = os => { switch (os) { @@ -30,32 +29,31 @@ const BackgroundTaskContext = React.createContext({} as ContextType export const BackgroundTaskProvider = ({ children }) => { const { localCert } = useCertificate(); - const { enqueueSnackbar, closeSnackbar } = useSnackbar(); + const { toast, dismiss } = useToast(); const downloadLogs = async (hostUri: string, dseq: string, gseq: number, oseq: number, isLogs: boolean) => { return new Promise((resolve, reject) => { const ws = new WebSocket(PROVIDER_PROXY_URL_WS); let isCancelled = false; let isFinished = false; + let snackbarKey: string; function onCancel() { isCancelled = true; ws.close(); - closeSnackbar(snackbarKey); + dismiss(snackbarKey); } - const snackbarKey = enqueueSnackbar( - - Cancel - - } - showLoading - />, - { variant: "info", persist: true, action: key => null } - ); + const { id } = toast({ + title: isLogs ? "Downloading logs..." : "Downloading events...", + description: ( + + ), + loading: true + }); + snackbarKey = id; const url = isLogs ? `${hostUri}/lease/${dseq}/${gseq}/${oseq}/logs?follow=false&tail=10000000` @@ -88,15 +86,15 @@ export const BackgroundTaskProvider = ({ children }) => { console.log("Cancelled"); resolve(true); } else if (isFinished) { - closeSnackbar(snackbarKey); + dismiss(snackbarKey); console.log("Done, downloading file"); const fileName = `${dseq}-${gseq}-${oseq}-logs-${new Date().toISOString().substring(0, 10)}.txt`; FileSaver.saveAs(new Blob([logFileContent]), fileName); resolve(true); } else { console.log("No logs / Failed"); - closeSnackbar(snackbarKey); - enqueueSnackbar("Failed to download logs", { variant: "error" }); + dismiss(snackbarKey); + toast({ title: "Failed to download logs", variant: "destructive" }); reject("Failed to download logs"); } }; @@ -105,8 +103,8 @@ export const BackgroundTaskProvider = ({ children }) => { JSON.stringify({ type: "websocket", url: url, - certPem: localCert.certPem, - keyPem: localCert.keyPem + certPem: localCert?.certPem, + keyPem: localCert?.keyPem }) ); }; @@ -126,25 +124,24 @@ export const BackgroundTaskProvider = ({ children }) => { const ws = new WebSocket(PROVIDER_PROXY_URL_WS); let isCancelled = false; let isFinished = false; + let snackbarKey: string; function onCancel() { isCancelled = true; ws.close(); - closeSnackbar(snackbarKey); + dismiss(snackbarKey); } - const snackbarKey = enqueueSnackbar( - - Cancel - - } - showLoading - />, - { variant: "info", persist: true, action: key => null } - ); + const { id } = toast({ + title: `Downloading ${filePath}...`, + description: ( + + ), + loading: true + }); + snackbarKey = id; const printCommand = getPrintCommand("linux"); const command = `${printCommand} ${filePath}`; @@ -198,15 +195,15 @@ export const BackgroundTaskProvider = ({ children }) => { ws.onclose = () => { if (isCancelled) { console.log("Cancelled"); - } else if (isFinished) { - closeSnackbar(snackbarKey); + } else if (isFinished && fileContent) { + dismiss(snackbarKey); console.log("Done, downloading file"); const filename = filePath.replace(/^.*[\\\/]/, ""); FileSaver.saveAs(new Blob([fileContent]), filename); } else { console.log("No file / Failed"); - closeSnackbar(snackbarKey); - enqueueSnackbar("Failed to download file", { variant: "error" }); + dismiss(snackbarKey); + toast({ title: "Failed to download file", variant: "destructive" }); } }; ws.onopen = () => { diff --git a/deploy-web/src/context/ChainParamProvider/ChainParamProvider.tsx b/deploy-web/src/context/ChainParamProvider/ChainParamProvider.tsx index 847b91adb..719b5f44d 100644 --- a/deploy-web/src/context/ChainParamProvider/ChainParamProvider.tsx +++ b/deploy-web/src/context/ChainParamProvider/ChainParamProvider.tsx @@ -1,3 +1,4 @@ +"use client"; import React from "react"; import { useEffect } from "react"; import { uAktDenom } from "@src/utils/constants"; diff --git a/deploy-web/src/context/CustomThemeContext/CustomThemeContext.tsx b/deploy-web/src/context/CustomThemeContext/CustomThemeContext.tsx index fbe3f0b4e..b82d627df 100644 --- a/deploy-web/src/context/CustomThemeContext/CustomThemeContext.tsx +++ b/deploy-web/src/context/CustomThemeContext/CustomThemeContext.tsx @@ -5,8 +5,8 @@ import { createTheme, ThemeProvider, ThemeOptions } from "@mui/material/styles"; import CssBaseline from "@mui/material/CssBaseline"; import { customColors } from "@src/utils/colors"; import { grey } from "@mui/material/colors"; -import { useDarkMode } from "next-dark-mode"; import { accountBarHeight } from "@src/utils/constants"; +import { useTheme } from "next-themes"; type ContextType = { mode: string; @@ -21,7 +21,7 @@ const getDesignTokens = (mode: PaletteMode): ThemeOptions => ({ ? { // LIGHT primary: { - main: customColors.dark + main: "hsl(var(--primary))" }, secondary: { main: customColors.main @@ -38,7 +38,7 @@ const getDesignTokens = (mode: PaletteMode): ThemeOptions => ({ : { // DARK primary: { - main: customColors.dark + main: "hsl(var(--primary))" }, secondary: { main: customColors.main @@ -57,9 +57,6 @@ const getDesignTokens = (mode: PaletteMode): ThemeOptions => ({ } }) }, - typography: { - fontFamily: ["Inter", "sans-serif"].join(",") - }, breakpoints: { values: { xs: 0, @@ -71,65 +68,73 @@ const getDesignTokens = (mode: PaletteMode): ThemeOptions => ({ }, components: { MuiCssBaseline: { - // styleOverrides: { - // html: { - // scrollPaddingTop: `${accountBarHeight}px`, - // WebkitFontSmoothing: "auto", - // height: "100%", - // width: "100%" - // }, - // body: { - // height: `calc(100% - ${accountBarHeight}px) !important`, - // width: "100%", - // overflowY: "scroll !important", - // padding: "0 !important", - // "&::-webkit-scrollbar": { - // width: "10px" - // }, - // "&::-webkit-scrollbar-track": { - // background: mode === "dark" ? darken(customColors.dark, 0.2) : customColors.white - // }, - // "&::-webkit-scrollbar-thumb": { - // width: "5px", - // backgroundColor: mode === "dark" ? lighten(customColors.darkLight, 0.2) : grey[500], - // borderRadius: "5px" - // } - // }, - // "*": { - // transition: "background-color .2s ease" - // }, - // ul: { - // paddingLeft: "2rem" - // }, - // // Nextjs root div - // "#__next": { - // height: "100%" - // }, - // // Page loading styling - // "#nprogress .bar": { - // background: `${customColors.main} !important`, - // zIndex: "10000 !important" - // }, - // "#nprogress .spinner": { - // zIndex: `10000 !important`, - // top: "6px !important", - // right: "8px !important" - // }, - // "#nprogress .peg": { - // boxShadow: `0 0 10px ${customColors.main}, 0 0 5px ${customColors.main}` - // }, - // "#nprogress .spinner-icon": { - // borderTopColor: `${customColors.main} !important`, - // borderLeftColor: `${customColors.main} !important` - // }, - // a: { - // textDecoration: "none", - // color: customColors.main, - // "&:hover": { - // textDecoration: "underline" - // } - // } - // } + styleOverrides: { + // html: { + // scrollPaddingTop: `${accountBarHeight}px`, + // WebkitFontSmoothing: "auto", + // height: "100%", + // width: "100%" + // }, + // body: { + // height: `calc(100% - ${accountBarHeight}px) !important`, + // width: "100%", + // overflowY: "scroll !important", + // padding: "0 !important", + // "&::-webkit-scrollbar": { + // width: "10px" + // }, + // "&::-webkit-scrollbar-track": { + // background: mode === "dark" ? darken(customColors.dark, 0.2) : customColors.white + // }, + // "&::-webkit-scrollbar-thumb": { + // width: "5px", + // backgroundColor: mode === "dark" ? lighten(customColors.darkLight, 0.2) : grey[500], + // borderRadius: "5px" + // } + // }, + // "*": { + // transition: "background-color .2s ease" + // }, + // ul: { + // paddingLeft: "2rem" + // }, + // Nextjs root div + "#__next": { + height: "100%" + }, + // Page loading styling + "#nprogress .bar": { + background: `hsl(var(--primary)) !important`, + height: "2px !important", + zIndex: "10000 !important" + }, + "#nprogress .spinner": { + zIndex: `10000 !important`, + top: "6px !important", + right: "8px !important" + }, + "#nprogress .peg": { + boxShadow: `0 0 10px hsl(var(--primary)), 0 0 5pxhsl(var(--primary))` + }, + "#nprogress .spinner-icon": { + borderTopColor: `hsl(var(--primary)) !important`, + borderLeftColor: `hsl(var(--primary)) !important` + } + // a: { + // textDecoration: "none", + // color: customColors.main, + // "&:hover": { + // textDecoration: "underline" + // } + // } + } + }, + MuiLinearProgress: { + styleOverrides: { + root: { + backgroundColor: "hsl(var(--primary) / 15%)" + } + } }, MuiPaper: { styleOverrides: { @@ -197,7 +202,9 @@ const lightTheme = createTheme(getDesignTokens("light")); export const CustomThemeProvider = ({ children }) => { const [isMounted, setIsMounted] = useState(false); - const { darkModeActive } = useDarkMode(); + // const { darkModeActive } = useDarkMode(); + const { theme: nextTheme } = useTheme(); + const darkModeActive = nextTheme === "dark"; const mode = darkModeActive ? "dark" : "light"; // Update the theme only if the mode changes const theme = React.useMemo(() => (darkModeActive ? darkTheme : lightTheme), [darkModeActive]); diff --git a/deploy-web/src/context/SettingsProvider/SettingsProviderContext.tsx b/deploy-web/src/context/SettingsProvider/SettingsProviderContext.tsx index 44e021f60..9c3c65202 100644 --- a/deploy-web/src/context/SettingsProvider/SettingsProviderContext.tsx +++ b/deploy-web/src/context/SettingsProvider/SettingsProviderContext.tsx @@ -2,12 +2,13 @@ import React, { useEffect, useState, useCallback } from "react"; import axios from "axios"; import { queryClient } from "@src/queries"; -import { mainnetId, mainnetNodes } from "@src/utils/constants"; +import { mainnetId } from "@src/utils/constants"; import { useLocalStorage } from "@src/hooks/useLocalStorage"; import { initAppTypes } from "@src/utils/init"; import { NodeStatus } from "@src/types/node"; import { initiateNetworkData, networks } from "@src/store/networkStore"; import { migrateLocalStorage } from "@src/utils/localStorage"; +import { mainnetNodes } from "@src/utils/apiUtils"; export type BlockchainNode = { api: string; @@ -49,7 +50,7 @@ const defaultSettings: Settings = { customNode: null }; -export function SettingsProvider ({ children, version }: React.PropsWithChildren<{ version: string }>) { +export function SettingsProvider({ children }: React.PropsWithChildren<{}>) { const [settings, setSettings] = useState(defaultSettings); const [isLoadingSettings, setIsLoadingSettings] = useState(true); const [isSettingsInit, setIsSettingsInit] = useState(false); @@ -67,7 +68,7 @@ export function SettingsProvider ({ children, version }: React.PropsWithChildren await initiateNetworkData(); // Apply local storage migrations - migrateLocalStorage(version); + migrateLocalStorage(); // Init app types based on the selected network id initAppTypes(); @@ -313,7 +314,7 @@ export function SettingsProvider ({ children, version }: React.PropsWithChildren {children} ); -}; +} export const useSettings = () => { return { ...React.useContext(SettingsProviderContext) }; diff --git a/deploy-web/src/context/WalletProvider/WalletProvider.tsx b/deploy-web/src/context/WalletProvider/WalletProvider.tsx index 88603fdb6..ecc158f37 100644 --- a/deploy-web/src/context/WalletProvider/WalletProvider.tsx +++ b/deploy-web/src/context/WalletProvider/WalletProvider.tsx @@ -141,7 +141,7 @@ export const WalletProvider = ({ children }) => { async function loadWallet(): Promise { const selectedNetwork = getSelectedNetwork(); - const storageWallets = JSON.parse(localStorage.getItem(`${selectedNetwork.id}/wallets`) || "") as LocalWalletDataType[]; + const storageWallets = JSON.parse(localStorage.getItem(`${selectedNetwork.id}/wallets`) || "[]") as LocalWalletDataType[]; let currentWallets = storageWallets ?? []; @@ -155,10 +155,7 @@ export const WalletProvider = ({ children }) => { await refreshBalances(); - setTimeout(() => { - setIsWalletLoaded(true); - }, 10000); - // setIsWalletLoaded(true); + setIsWalletLoaded(true); } async function signAndBroadcastTx(msgs: EncodeObject[]): Promise { diff --git a/deploy-web/src/hooks/useCustomUser.ts b/deploy-web/src/hooks/useCustomUser.ts index 4738cf434..a34b9320c 100644 --- a/deploy-web/src/hooks/useCustomUser.ts +++ b/deploy-web/src/hooks/useCustomUser.ts @@ -5,7 +5,7 @@ import { plans } from "@src/utils/plans"; type UseCustomUser = { user: CustomUserProfile; isLoading: boolean; - error: Error; + error: Error | undefined; checkSession: () => Promise; }; diff --git a/deploy-web/src/pages/404.tsx b/deploy-web/src/pages/404.tsx index 85050e116..a560d8003 100644 --- a/deploy-web/src/pages/404.tsx +++ b/deploy-web/src/pages/404.tsx @@ -1,43 +1,35 @@ import Layout from "../components/layout/Layout"; import { ReactNode } from "react"; -import PageContainer from "@src/components/shared/PageContainer"; -import { Box, Button, Typography, useTheme } from "@mui/material"; +import { PageContainer } from "@src/components/shared/PageContainer"; import { Title } from "@src/components/shared/Title"; import { NextSeo } from "next-seo"; import { UrlService } from "@src/utils/urlUtils"; import Link from "next/link"; -import ArrowForwardIcon from "@mui/icons-material/ArrowForward"; +import { buttonVariants } from "@src/components/ui/button"; +import { cn } from "@src/utils/styleUtils"; +import { ArrowLeft } from "iconoir-react"; type Props = { children?: ReactNode; }; const FourOhFour: React.FunctionComponent = ({}) => { - const theme = useTheme(); - return ( - - 404 - - + <div className="mt-10 text-center"> + <Title className="mb-2">404 +

Page not found.

- - - -
+
+ + + Go to homepage + +
+
); diff --git a/deploy-web/src/pages/_app.tsx b/deploy-web/src/pages/_app.tsx new file mode 100644 index 000000000..3747d30a8 --- /dev/null +++ b/deploy-web/src/pages/_app.tsx @@ -0,0 +1,144 @@ +import React, { useEffect } from "react"; +import Router from "next/router"; +import NProgress from "nprogress"; //nprogress module +import "nprogress/nprogress.css"; //styles of nprogress +import { CacheProvider, EmotionCache } from "@emotion/react"; +import createEmotionCache from "@src/utils/createEmotionCache"; +import { ColorModeProvider } from "@src/context/CustomThemeContext"; +import { CustomSnackbarProvider } from "@src/context/CustomSnackbarProvider"; +import { AppProps } from "next/app"; +// import withDarkMode from "next-dark-mode"; +import "../styles/index.css"; +import { QueryClientProvider } from "react-query"; +import { queryClient } from "@src/queries"; +import { WalletProvider } from "@src/context/WalletProvider"; +import { PricingProvider } from "@src/context/PricingProvider/PricingProvider"; +import { BackgroundTaskProvider } from "@src/context/BackgroundTaskProvider"; +import { SettingsProvider } from "@src/context/SettingsProvider"; +import { CertificateProvider } from "@src/context/CertificateProvider"; +import { TemplatesProvider } from "@src/context/TemplatesProvider"; +import { LocalNoteProvider } from "@src/context/LocalNoteProvider"; +import { isProd } from "@src/utils/constants"; +import { GoogleAnalytics } from "nextjs-google-analytics"; +import { UserProvider } from "@auth0/nextjs-auth0/client"; +import { AddressBookProvider } from "@src/context/AddressBookProvider"; +import { Provider } from "jotai"; +import { usePreviousRoute } from "@src/hooks/usePreviousRoute"; +import { PageHead } from "@src/components/layout/PageHead"; +import { CustomChainProvider } from "@src/context/CustomChainProvider"; + +import "../styles/globals.css"; +import "../styles/index.css"; + +import { ChainParamProvider } from "@src/context/ChainParamProvider"; +import { CustomIntlProvider } from "@src/components/layout/CustomIntlProvider"; +import { ThemeProvider } from "next-themes"; +import { TooltipProvider } from "@src/components/ui/tooltip"; +import { cn } from "@src/utils/styleUtils"; +import { GeistSans } from "geist/font/sans"; + +interface Props extends AppProps { + emotionCache?: EmotionCache; +} + +NProgress.configure({ + minimum: 0.2 +}); + +//Binding events. +Router.events.on("routeChangeStart", () => NProgress.start()); +Router.events.on("routeChangeComplete", () => NProgress.done()); +Router.events.on("routeChangeError", () => NProgress.done()); + +// Client-side cache, shared for the whole session of the user in the browser. +const clientSideEmotionCache = createEmotionCache(); + +const App: React.FunctionComponent = ({ Component, pageProps, emotionCache = clientSideEmotionCache }) => { + usePreviousRoute(); + + return ( +
+ + + {/* + + + + + + + + + + + + + + + + {isProd && } + + + + + + + + + + + + + + + + + */} + + + + + + {/* */} + + + + + + + + + + + + + + + {/* */} + + {isProd && } + + + + + + + + + + + + + + + + {/* */} + + + + +
+ ); +}; + +export default App; diff --git a/deploy-web/src/pages/_document.tsx b/deploy-web/src/pages/_document.tsx new file mode 100644 index 000000000..03a851931 --- /dev/null +++ b/deploy-web/src/pages/_document.tsx @@ -0,0 +1,106 @@ +// pages/_document.js +import createEmotionCache from "@src/utils/createEmotionCache"; +import Document, { Head, Main, NextScript, Html } from "next/document"; +import createEmotionServer from "@emotion/server/create-instance"; +import React from "react"; +import { customColors } from "@src/utils/colors"; +import { cn } from "@src/utils/styleUtils"; +// import { cookies } from "next/headers"; + +/** + * 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 : "light"; +// return theme; +// } + +export default class MyDocument extends Document { + render() { + return ( + + + {/* PWA */} + + + + + + + + + + {/* Inject MUI styles first to match with the prepend: true configuration. */} + {(this.props as any).emotionStyleTags} + + +
+ + + + ); + } +} + +// `getInitialProps` belongs to `_document` (instead of `_app`), +// it's compatible with static-site generation (SSG). +MyDocument.getInitialProps = async ctx => { + // Resolution order + // + // On the server: + // 1. app.getInitialProps + // 2. page.getInitialProps + // 3. document.getInitialProps + // 4. app.render + // 5. page.render + // 6. document.render + // + // On the server with error: + // 1. document.getInitialProps + // 2. app.render + // 3. page.render + // 4. document.render + // + // On the client + // 1. app.getInitialProps + // 2. page.getInitialProps + // 3. app.render + // 4. page.render + + const originalRenderPage = ctx.renderPage; + + // You can consider sharing the same emotion cache between all the SSR requests to speed up performance. + // However, be aware that it can have global side effects. + const cache = createEmotionCache(); + const { extractCriticalToChunks } = createEmotionServer(cache); + + ctx.renderPage = () => + originalRenderPage({ + enhanceApp: (App: any) => + function EnhanceApp(props) { + return ; + } + }); + + const initialProps = await Document.getInitialProps(ctx); + // This is important. It prevents emotion to render invalid HTML. + // See https://github.com/mui/material-ui/issues/26561#issuecomment-855286153 + const emotionStyles = extractCriticalToChunks(initialProps.html); + const emotionStyleTags = emotionStyles.styles.map(style => ( +