From d91d340212c38436a7266f8be6205d3b0633f9f6 Mon Sep 17 00:00:00 2001 From: Jigar Patel Date: Wed, 16 Oct 2024 16:11:59 -0400 Subject: [PATCH] fixed refresh token, added disabled menu items --- apps/provider-console/sentry.client.config.js | 1 + .../dashboard/DashboardCardSkeleton.tsx | 21 ++ .../components/dashboard/stat-pie-charts.tsx | 13 +- .../src/components/home/HomeContainer.tsx | 2 +- .../src/components/layout/Sidebar.tsx | 38 ++- .../components/layout/SidebarRouteButton.tsx | 3 +- .../src/pages/dashboard/index.tsx | 245 ++++++++++++------ apps/provider-console/src/types/index.ts | 1 + .../src/utils/consoleClient.ts | 4 +- apps/provider-console/src/utils/constants.ts | 5 +- apps/provider-console/src/utils/formatUsd.ts | 18 ++ apps/provider-console/src/utils/restClient.ts | 21 -- apps/provider-console/src/utils/tokenUtils.ts | 6 +- 13 files changed, 269 insertions(+), 109 deletions(-) create mode 100644 apps/provider-console/src/components/dashboard/DashboardCardSkeleton.tsx create mode 100644 apps/provider-console/src/utils/formatUsd.ts diff --git a/apps/provider-console/sentry.client.config.js b/apps/provider-console/sentry.client.config.js index fc43080d6..37187678a 100644 --- a/apps/provider-console/sentry.client.config.js +++ b/apps/provider-console/sentry.client.config.js @@ -2,6 +2,7 @@ import * as Sentry from "@sentry/nextjs"; Sentry.init({ dsn: "https://e756e9e5316f88fa972329632f5e6434@sentry.praetorapp.com/2", + enabled: "false", integrations: [Sentry.replayIntegration()], replaysSessionSampleRate: 0.1, replaysOnErrorSampleRate: 1.0 diff --git a/apps/provider-console/src/components/dashboard/DashboardCardSkeleton.tsx b/apps/provider-console/src/components/dashboard/DashboardCardSkeleton.tsx new file mode 100644 index 000000000..c93767f52 --- /dev/null +++ b/apps/provider-console/src/components/dashboard/DashboardCardSkeleton.tsx @@ -0,0 +1,21 @@ +import React from "react"; +import { Card, CardContent } from "@akashnetwork/ui/components"; + +const DashboardCardSkeleton: React.FC = () => ( + + +
+
+
+
+
+
+
+
+
+
+
+
+); + +export default DashboardCardSkeleton; diff --git a/apps/provider-console/src/components/dashboard/stat-pie-charts.tsx b/apps/provider-console/src/components/dashboard/stat-pie-charts.tsx index f3bd9c7e0..ac7b3584d 100644 --- a/apps/provider-console/src/components/dashboard/stat-pie-charts.tsx +++ b/apps/provider-console/src/components/dashboard/stat-pie-charts.tsx @@ -53,7 +53,18 @@ export const StatPieChart: React.FC = ({ activeResources, pen - + {data.map((entry, index) => ( ))} diff --git a/apps/provider-console/src/components/home/HomeContainer.tsx b/apps/provider-console/src/components/home/HomeContainer.tsx index bee981626..b02efbaa3 100644 --- a/apps/provider-console/src/components/home/HomeContainer.tsx +++ b/apps/provider-console/src/components/home/HomeContainer.tsx @@ -55,7 +55,7 @@ export function HomeContainer() {
- {(!isProviderStatusFetched || isLoading) ? ( + {(!isProviderStatusFetched || isLoading) && isWalletConnected ? (

{loadingMessage}

diff --git a/apps/provider-console/src/components/layout/Sidebar.tsx b/apps/provider-console/src/components/layout/Sidebar.tsx index 9934c9a8d..2d0224dfc 100644 --- a/apps/provider-console/src/components/layout/Sidebar.tsx +++ b/apps/provider-console/src/components/layout/Sidebar.tsx @@ -4,7 +4,7 @@ import { Button, buttonVariants } from "@akashnetwork/ui/components"; import Drawer from "@mui/material/Drawer"; import { useTheme as useMuiTheme } from "@mui/material/styles"; import useMediaQuery from "@mui/material/useMediaQuery"; -import { Discord, Github, Menu, MenuScale, Rocket, X as TwitterX, Youtube } from "iconoir-react"; +import { Discord, Github, Menu, MenuScale, Rocket, X as TwitterX, Youtube, Cloud, Settings, ClipboardCheck, Calculator, ListSelect } from "iconoir-react"; import { Home, OpenInWindow } from "iconoir-react"; import getConfig from "next/config"; import Image from "next/image"; @@ -43,6 +43,41 @@ export const Sidebar: React.FunctionComponent = ({ isMobileOpen, handleDr icon: props => , url: UrlService.home(), activeRoutes: [UrlService.home()] + }, + { + title: "Leases", + icon: props => , + url: "#", + activeRoutes: ["#"], + disabled: true + }, + { + title: "Actions", + icon: props => , + url: "#", + activeRoutes: ["#"], + disabled: true + }, + { + title: "Pricing", + icon: props => , + url: "#", + activeRoutes: ["#"], + disabled: true + }, + { + title: "Attributes", + icon: props => , + url: "#", + activeRoutes: ["#"], + disabled: true + }, + { + title: "Settings", + icon: props => , + url: "#", + activeRoutes: ["#"], + disabled: true } ] }, @@ -238,4 +273,3 @@ export const Sidebar: React.FunctionComponent = ({ isMobileOpen, handleDr ); }; - diff --git a/apps/provider-console/src/components/layout/SidebarRouteButton.tsx b/apps/provider-console/src/components/layout/SidebarRouteButton.tsx index 681be8099..319a59714 100644 --- a/apps/provider-console/src/components/layout/SidebarRouteButton.tsx +++ b/apps/provider-console/src/components/layout/SidebarRouteButton.tsx @@ -31,7 +31,8 @@ export const SidebarRouteButton: React.FunctionComponent = ({ route, clas { ["font-bold"]: isSelected, ["min-w-[initial] px-4 py-1"]: isNavOpen, - ["w-[45px] min-w-0 p-2"]: !isNavOpen + ["w-[45px] min-w-0 p-2"]: !isNavOpen, + ["pointer-events-none opacity-50"]: route.disabled // Add this line } )} > diff --git a/apps/provider-console/src/pages/dashboard/index.tsx b/apps/provider-console/src/pages/dashboard/index.tsx index 19cd15d6b..0327d1179 100644 --- a/apps/provider-console/src/pages/dashboard/index.tsx +++ b/apps/provider-console/src/pages/dashboard/index.tsx @@ -16,6 +16,9 @@ import { StatPieChart } from "@src/components/dashboard/stat-pie-charts"; import { useSelectedChain } from "@src/context/CustomChainProvider"; import { formatBytes } from "@src/utils/formatBytes"; import withAuth from "@src/components/shared/withAuth"; +import { formatUUsd } from "@src/utils/formatUsd"; +import DashboardCardSkeleton from "@src/components/dashboard/DashboardCardSkeleton"; +import { useWallet } from "@src/context/WalletProvider"; // Moved outside component to avoid recreation on each render const fetchAktPrice = async () => { @@ -30,15 +33,33 @@ const fetchAktPrice = async () => { } }; +const calculatePercentageChange = (currentPrice: number | null, previousPrice: number | null) => { + if (currentPrice === null || previousPrice === null || previousPrice === 0) { + return 0%; + } + + const percentageChange = ((currentPrice - previousPrice) / previousPrice) * 100; + const formattedChange = Math.abs(percentageChange).toFixed(2); + + if (percentageChange > 0) { + return +{formattedChange}%; + } else if (percentageChange < 0) { + return -{formattedChange}%; + } else { + return 0%; + } +}; + const Dashboard: React.FC = () => { const [providerActions, setProviderActions] = useState([]); const [aktPrice, setAktPrice] = useState(null); const { address } = useSelectedChain(); + const { isOnline } = useWallet(); // Add this query to fetch provider details const { data: providerDetails, isLoading: isLoadingProviderDetails }: { data: any; isLoading: boolean } = useQuery( "providerDetails", - () => consoleClient.get(`/providers/${address}`), + () => consoleClient.get(`/v1/providers/${address}`), { // You might want to adjust these options based on your needs refetchOnWindowFocus: false, @@ -46,6 +67,16 @@ const Dashboard: React.FC = () => { } ); + // Add this new query to fetch provider dashboard details + const { data: providerDashboard, isLoading: isLoadingProviderDashboard }: { data: any; isLoading: boolean } = useQuery( + "providerDashboard", + () => consoleClient.get(`/internal/provider-dashboard/${address}`), + { + refetchOnWindowFocus: false, + retry: 3 + } + ); + useEffect(() => { const fetchData = async () => { const [price, actions]: [string, any] = await Promise.all([fetchAktPrice(), restClient.get("/actions")]); @@ -108,94 +139,156 @@ const Dashboard: React.FC = () => {
Provider Summary
-
- - -
-
-
Total Paid 24H
-
$2555.0
-
+3.35%
-
-
-
- +
+ {isLoadingProviderDashboard ? ( + <> + + + + + + ) : ( + <> + + +
+
+
Total Paid 24H
+
{formatUUsd(providerDashboard?.current.dailyUUsdEarned)}
+
+ + + + {calculatePercentageChange(providerDashboard?.current.dailyUUsdEarned, providerDashboard?.previous.dailyUUsdEarned)} + + +

Change in total paid compared to 24 hours ago

+
+
+
+
+
+
+
+ {/* */} +
+
-
-
- - - - -
-
-
Total Paid 7D
-
$7354.0
-
+7.35%
-
-
-
- + + + + +
+
+
Total Paid
+
{formatUUsd(providerDashboard?.current.totalUUsdEarned)}
+
+ + + + {calculatePercentageChange(providerDashboard?.current.totalUUsdEarned, providerDashboard?.previous.totalUUsdEarned)} + + +

Change in total paid compared to 24 hours ago

+
+
+
+
+
+
+
+ {/* */} +
+
-
-
- - - - -
-
-
Total Leases
-
18
-
+5.70%
-
-
-
- + + + + +
+
+
Active Leases
+
+ {providerDashboard?.current.activeLeaseCount ? `${providerDashboard?.current.activeLeaseCount}` : "0"} +
+
+ + + + {calculatePercentageChange(providerDashboard?.current.activeLeaseCount, providerDashboard?.previous.activeLeaseCount)} + + +

Change in active leases compared to 24 hours ago

+
+
+
+
+
+
+
+ {/* */} +
+
-
-
- - + + + + +
+
+
Total Leases
+
+ {providerDashboard?.current.totalLeaseCount ? `${providerDashboard?.current.totalLeaseCount}` : "0"} +
+
+ + + + {calculatePercentageChange(providerDashboard?.current.totalLeaseCount, providerDashboard?.previous.totalLeaseCount)} + + +

Change in total leases compared to 24 hours ago

+
+
+
+
+
+
+
+ {/* */} +
+
+
+
+
+ + )}
- {providerDetails && providerDetails.isOnline && ( + {isOnline && ( <>
Resources Leased Summary
-
{isLoadingProviderDetails ?
Loading resource details...
: renderResourceCards(providerDetails)}
+
+ {isLoadingProviderDetails ? ( +
+ + + + +
+ ) : ( + renderResourceCards(providerDetails) + )} +
)} + +
-
Spent Assets Summary
-
- - -
AKT Spent
-
- -
-
-
$2555.0
-
3.35%
-
-
Graph here
-
-
-
- - Total Paid - $2555.0 - - - Total Paid - $2555.0 - -
-
-
Provider Activity
+
Provider Actions
diff --git a/apps/provider-console/src/types/index.ts b/apps/provider-console/src/types/index.ts index 2242572e6..534400d85 100644 --- a/apps/provider-console/src/types/index.ts +++ b/apps/provider-console/src/types/index.ts @@ -33,4 +33,5 @@ export type ISidebarRoute = { isNew?: boolean; rel?: string; target?: string; + disabled?: boolean; }; diff --git a/apps/provider-console/src/utils/consoleClient.ts b/apps/provider-console/src/utils/consoleClient.ts index bc7ff6356..9bf8208a7 100644 --- a/apps/provider-console/src/utils/consoleClient.ts +++ b/apps/provider-console/src/utils/consoleClient.ts @@ -1,14 +1,12 @@ // import { notification } from "antd"; import axios from "axios"; -import authClient from "./authClient"; - const errorNotification = (error = "Error Occurred") => { console.log(error); }; const consoleClient = axios.create({ - baseURL: `https://console-api.akash.network/v1`, + baseURL: `https://api-preview.cloudmos.io`, timeout: 60000 }); diff --git a/apps/provider-console/src/utils/constants.ts b/apps/provider-console/src/utils/constants.ts index 6fc228939..a4275dfa8 100644 --- a/apps/provider-console/src/utils/constants.ts +++ b/apps/provider-console/src/utils/constants.ts @@ -58,8 +58,9 @@ function getApiUrl() { // if (typeof window === "undefined") return "http://localhost:3080"; // if (productionHostnames.includes(window.location?.hostname)) { try { - const _selectedNetworkId = localStorage.getItem("selectedNetworkId"); - return getNetworkBaseApiUrl(_selectedNetworkId || mainnetId); + // TODO: Revisit this when we have a way to get the selected network from the wallet + // const _selectedNetworkId = localStorage.getItem("selectedNetworkId"); + return getNetworkBaseApiUrl(mainnetId); } catch (e) { console.error(e); return productionMainnetApiUrl; diff --git a/apps/provider-console/src/utils/formatUsd.ts b/apps/provider-console/src/utils/formatUsd.ts new file mode 100644 index 000000000..a91638a1d --- /dev/null +++ b/apps/provider-console/src/utils/formatUsd.ts @@ -0,0 +1,18 @@ +/** + * Formats a USD amount using K (thousands) and M (millions) abbreviations. + * @param amount The amount to format in cents (integer) + * @param decimals The number of decimal places to show (default: 2) + * @returns Formatted USD string + */ +export function formatUUsd(amount: number, decimals: number = 2): string { + const dollars = amount / 1000000; // Convert cents to dollars + + if (dollars >= 1000000) { + return `$${(dollars / 1000000).toFixed(decimals)}M`; + } else if (dollars >= 1000) { + return `$${(dollars / 1000).toFixed(decimals)}K`; + } else { + return `$${dollars.toFixed(2)}`; + } +} + diff --git a/apps/provider-console/src/utils/restClient.ts b/apps/provider-console/src/utils/restClient.ts index c67b00f84..1fe9b1017 100644 --- a/apps/provider-console/src/utils/restClient.ts +++ b/apps/provider-console/src/utils/restClient.ts @@ -30,27 +30,6 @@ restClient.interceptors.response.use( // The request was made and the server responded with a status code // that falls out of the range of 2xx - const originalRequest = error.config; - - if (error.response.status === 401 && error.response.data.detail === "Signature has expired" && !originalRequest.retry) { - originalRequest.retry = true; - - try { - const newToken = await checkAndRefreshToken(); - if (newToken) { - originalRequest.headers.Authorization = `Bearer ${newToken}`; - return restClient(originalRequest); - } else { - // Token refresh failed, redirect to login or handle accordingly - // For example: history.push("/auth/login"); - throw new Error("Token refresh failed"); - } - } catch (refreshError) { - console.error("Error refreshing token:", refreshError); - throw refreshError; - } - } - if (error.response.status === 401 && error.response.data.detail !== "Signature has expired") { console.log(error) // purgeStorage(); diff --git a/apps/provider-console/src/utils/tokenUtils.ts b/apps/provider-console/src/utils/tokenUtils.ts index 9a3d96d40..7331ab846 100644 --- a/apps/provider-console/src/utils/tokenUtils.ts +++ b/apps/provider-console/src/utils/tokenUtils.ts @@ -29,12 +29,14 @@ export async function checkAndRefreshToken(): Promise { throw new Error('Refresh token or wallet address not found'); } - const refreshResponse = await authClient.post('/auth/refresh', { + const refreshResponse: any = await authClient.post('/auth/refresh', { refresh_token: refreshToken, address: walletAddress, }); - if (refreshResponse.data.status === 'success') { + console.log("refreshResponse", refreshResponse); + + if (refreshResponse.status === 'success') { const newAccessToken = refreshResponse.data.access_token; const newRefreshToken = refreshResponse.data.refresh_token;