From c5efbff4bb45ea7eac58c174ca1456a70f2ae556 Mon Sep 17 00:00:00 2001 From: Jigar Patel <97186961+jigar-arc10@users.noreply.github.com> Date: Thu, 16 Jan 2025 02:07:34 +0530 Subject: [PATCH] fix(provider): Copy changes and refactor some components (#530) * chore: removed akt price data from dashboard * chore: refactor and fix copy changes * chore: changed actions to activity and other refactor changes * chore: change control machine to label connected * chore: refactor control machine sidebar * chore: refactor sidebar control machine and provider status * refactor(provider): change become provider process copy changes * refactor(provider): fix sidebar ui * refactor(provider): fix layout and added loading * refactor(provider): fix wallet import mode and api calls * fix: fixed type definations * fix: removed console logs --- .../become-provider/ProviderAttributes.tsx | 8 +- .../become-provider/ProviderConfig.tsx | 3 +- .../become-provider/ProviderPricing.tsx | 132 ++++++++---- .../become-provider/ServerAccess.tsx | 23 +- .../components/become-provider/ServerForm.tsx | 64 +++--- .../become-provider/WalletImport.tsx | 204 ++++++++++++------ .../src/components/dashboard/FinanceCard.tsx | 24 +-- .../src/components/home/HomeContainer.tsx | 6 +- .../src/components/icons/AkashConsoleLogo.tsx | 128 ++--------- .../layout/ControlMachineStatus.tsx | 32 +++ .../src/components/layout/Layout.tsx | 5 +- .../src/components/layout/ProviderStatus.tsx | 46 ++++ .../src/components/layout/Sidebar.tsx | 72 +++---- ...tionDetails.tsx => ActivityLogDetails.tsx} | 2 +- ...iderActionList.tsx => ActivityLogList.tsx} | 18 +- .../{actions => activity-logs}/[id]/index.tsx | 8 +- .../{actions => activity-logs}/index.tsx | 10 +- .../src/pages/dashboard/index.tsx | 94 +++----- .../src/queries/useProviderQuery.ts | 6 +- apps/provider-console/src/utils/formatUsd.ts | 4 +- apps/provider-console/src/utils/urlUtils.ts | 2 +- 21 files changed, 477 insertions(+), 414 deletions(-) create mode 100644 apps/provider-console/src/components/layout/ControlMachineStatus.tsx create mode 100644 apps/provider-console/src/components/layout/ProviderStatus.tsx rename apps/provider-console/src/components/shared/{ProviderActionDetails.tsx => ActivityLogDetails.tsx} (97%) rename apps/provider-console/src/components/shared/{ProviderActionList.tsx => ActivityLogList.tsx} (81%) rename apps/provider-console/src/pages/{actions => activity-logs}/[id]/index.tsx (67%) rename apps/provider-console/src/pages/{actions => activity-logs}/index.tsx (70%) diff --git a/apps/provider-console/src/components/become-provider/ProviderAttributes.tsx b/apps/provider-console/src/components/become-provider/ProviderAttributes.tsx index 49277c9f3..e70c5636e 100644 --- a/apps/provider-console/src/components/become-provider/ProviderAttributes.tsx +++ b/apps/provider-console/src/components/become-provider/ProviderAttributes.tsx @@ -17,7 +17,8 @@ import { SelectContent, SelectItem, SelectTrigger, - Separator } from "@akashnetwork/ui/components"; + Separator +} from "@akashnetwork/ui/components"; import { zodResolver } from "@hookform/resolvers/zod"; import { Plus, Trash } from "iconoir-react"; import { useAtom } from "jotai"; @@ -109,9 +110,8 @@ export const ProviderAttributes: React.FunctionComponent

{existingAttributes ? "Edit Provider Attributes" : "Provider Attributes"}

-

- {existingAttributes ? "Please update your provider attributes." : "Please enter your provider attributes."} -

+

Attributes choosen here will be displayed publicly to the Console.

+

It will be used for filtering and querying providers during bid process.

diff --git a/apps/provider-console/src/components/become-provider/ProviderConfig.tsx b/apps/provider-console/src/components/become-provider/ProviderConfig.tsx index 40c6c1d72..8a7efcc38 100644 --- a/apps/provider-console/src/components/become-provider/ProviderConfig.tsx +++ b/apps/provider-console/src/components/become-provider/ProviderConfig.tsx @@ -61,7 +61,8 @@ export const ProviderConfig: React.FC = ({ onComplete }) =>

Provider Information

-

Please enter your provider details.

+

Hostname will be displayed publicly to the Console.

+

Email may be used for notifications.

diff --git a/apps/provider-console/src/components/become-provider/ProviderPricing.tsx b/apps/provider-console/src/components/become-provider/ProviderPricing.tsx index 9c3d53e12..3f99163a0 100644 --- a/apps/provider-console/src/components/become-provider/ProviderPricing.tsx +++ b/apps/provider-console/src/components/become-provider/ProviderPricing.tsx @@ -8,7 +8,6 @@ import { Button, Form, FormControl, - FormDescription, FormField, FormItem, FormLabel, @@ -31,7 +30,7 @@ interface ProviderPricingProps { resources?: { cpu: number; memory: number; - storage: string; + storage: number; persistentStorage: number; gpu: number; }; @@ -126,10 +125,10 @@ export const ProviderPricing: React.FC = ({ onComplete, ed defaultValues: editMode ? existingPricing : { + gpu: 100, cpu: 1.6, memory: 0.8, storage: 0.02, - gpu: 100, persistentStorage: 0.3, ipScalePrice: 5, endpointBidPrice: 0.5 @@ -172,6 +171,41 @@ export const ProviderPricing: React.FC = ({ onComplete, ed const estimatedEarnings = calculateEstimatedEarnings(watchValues); + const calculateDefaultEarnings = useCallback(() => { + const defaultPricing = { + gpu: 100, + cpu: 1.6, + memory: 0.8, + storage: 0.02, + persistentStorage: 0.3, + ipScalePrice: 5, + endpointBidPrice: 0.5 + }; + + const { cpu, memory, storage, gpu, persistentStorage, ipScalePrice, endpointBidPrice } = defaultPricing; + + const totalCpuEarnings = resources.cpu * cpu; + const totalMemoryEarnings = resources.memory * memory; + const totalStorageEarnings = resources.storage * storage; + const totalGpuEarnings = resources.gpu * gpu; + const totalPersistentStorageEarnings = resources.persistentStorage * persistentStorage; + const totalIpScaleEarnings = ipScalePrice; + const totalEndpointBidEarnings = endpointBidPrice; + + const totalEarnings = + totalCpuEarnings + + totalMemoryEarnings + + totalStorageEarnings + + totalGpuEarnings + + totalPersistentStorageEarnings + + totalIpScaleEarnings + + totalEndpointBidEarnings; + + return totalEarnings * 0.8; + }, [resources]); + + const competitiveEarnings = calculateDefaultEarnings(); + const updateProviderPricingAndProceed = async (data: any) => { setIsLoading(true); if (!editMode) { @@ -204,7 +238,9 @@ export const ProviderPricing: React.FC = ({ onComplete, ed

Provider Pricing

-

Set Provider Pricing to earn rewards

+

+ The prices you set here determine the price your provider bids with and total revenue it earns for you. +

@@ -215,18 +251,17 @@ export const ProviderPricing: React.FC = ({ onComplete, ed
( - CPU - Scale Bid Price - USD/thread-month + GPU
field.onChange(newValue)} - max={4} + max={500} step={0.01} className="w-full" /> @@ -235,8 +270,9 @@ export const ProviderPricing: React.FC = ({ onComplete, ed type="number" {...field} onChange={e => field.onChange(parseFloat(e.target.value))} - className="w-32" - step="0.001" + className="w-72" + step="0.01" + endIcon={USD/GPU-month} />
@@ -246,11 +282,10 @@ export const ProviderPricing: React.FC = ({ onComplete, ed ( - Memory - Scale Bid Price - USD/GB-month + CPU
= ({ onComplete, ed value={[field.value]} onValueChange={([newValue]) => field.onChange(newValue)} max={4} - step={0.001} + step={0.01} className="w-full" /> = ({ onComplete, ed type="number" {...field} onChange={e => field.onChange(parseFloat(e.target.value))} - className="w-32" + className="w-72" step="0.001" + endIcon={USD/thread-month} />
@@ -277,18 +313,17 @@ export const ProviderPricing: React.FC = ({ onComplete, ed ( - Storage - Scale Bid Price - USD/GB-month + Memory
field.onChange(newValue)} - max={0.1} + max={4} step={0.001} className="w-full" /> @@ -297,8 +332,9 @@ export const ProviderPricing: React.FC = ({ onComplete, ed type="number" {...field} onChange={e => field.onChange(parseFloat(e.target.value))} - className="w-32" + className="w-72" step="0.001" + endIcon={USD/GB-month} />
@@ -308,19 +344,18 @@ export const ProviderPricing: React.FC = ({ onComplete, ed ( - GPU - Scale Bid Price - USD/GPU-month + Ephemeral Storage
field.onChange(newValue)} - max={500} - step={0.01} + max={0.1} + step={0.001} className="w-full" /> = ({ onComplete, ed type="number" {...field} onChange={e => field.onChange(parseFloat(e.target.value))} - className="w-32" - step="0.01" + className="w-72" + step="0.001" + endIcon={USD/GB-month} />
@@ -343,7 +379,6 @@ export const ProviderPricing: React.FC = ({ onComplete, ed render={({ field }) => ( Persistent Storage - Scale Bid Price - USD/GB-month
= ({ onComplete, ed type="number" {...field} onChange={e => field.onChange(parseFloat(e.target.value))} - className="w-32" + className="w-72" step="0.01" + endIcon={USD/GB-month} />
@@ -383,7 +419,6 @@ export const ProviderPricing: React.FC = ({ onComplete, ed render={({ field }) => ( IP Scale Price - Scale Bid Price - USD/leased IP-month
= ({ onComplete, ed type="number" {...field} onChange={e => field.onChange(parseFloat(e.target.value))} - className="w-32" + className="w-72" step="0.1" + endIcon={USD/IP-month} />
@@ -414,7 +450,6 @@ export const ProviderPricing: React.FC = ({ onComplete, ed render={({ field }) => ( Endpoint Bid Price - Scale Bid Price - USD/port-month
= ({ onComplete, ed type="number" {...field} onChange={e => field.onChange(parseFloat(e.target.value))} - className="w-32" + className="w-72" step="0.01" + endIcon={USD/port-month} />
@@ -445,18 +481,24 @@ export const ProviderPricing: React.FC = ({ onComplete, ed

Resources

{Object.entries(resources).length > 0 ? ( - Object.entries(resources).map(([key, value]) => ( -
-
- - {key.replace(/([A-Z])/g, " $1").trim()} - - - {key === "cpu" || key === "gpu" ? value : Math.round(Number(value))} {key === "cpu" || key === "gpu" ? "" : "GB"} - + Object.entries(resources) + .sort(([keyA], [keyB]) => (keyA === "gpu" ? -1 : keyB === "gpu" ? 1 : 0)) + .map(([key, value]) => ( +
+
+ + {key === "storage" + ? "Ephemeral Storage" + : key === "cpu" || key === "gpu" + ? key.toUpperCase() + : key.replace(/([A-Z])/g, " $1").trim()} + + + {key === "cpu" || key === "gpu" ? value : Math.round(Number(value))} {key === "cpu" || key === "gpu" ? "" : "GB"} + +
-
- )) + )) ) : (

No resources information available.

)} @@ -474,7 +516,7 @@ export const ProviderPricing: React.FC = ({ onComplete, ed

Benchmark Price -
$19.05/month
+
${competitiveEarnings.toFixed(2)}/month
diff --git a/apps/provider-console/src/components/become-provider/ServerAccess.tsx b/apps/provider-console/src/components/become-provider/ServerAccess.tsx index aa7195221..a6ce77aaa 100644 --- a/apps/provider-console/src/components/become-provider/ServerAccess.tsx +++ b/apps/provider-console/src/components/become-provider/ServerAccess.tsx @@ -2,7 +2,6 @@ import React, { useCallback, useState } from "react"; import { Button, Input, Separator } from "@akashnetwork/ui/components"; -import { ResetProviderForm } from "./ResetProviderProcess"; import { ServerForm } from "./ServerForm"; interface ServerAccessProps { @@ -30,18 +29,26 @@ export const ServerAccess: React.FC = ({ onComplete }) => {
{!activateServerForm ? (
-
-

Specify Number of Servers

-

Tell us how many servers you'll be using in your setup.

+
+

Server Count

+
- +

+ How many servers will you be using to set up this provider?
+ (Include all nodes - control nodes, etcd, worker nodes) +

-
- -
+
diff --git a/apps/provider-console/src/components/become-provider/ServerForm.tsx b/apps/provider-console/src/components/become-provider/ServerForm.tsx index 3aee927c6..5d255977a 100644 --- a/apps/provider-console/src/components/become-provider/ServerForm.tsx +++ b/apps/provider-console/src/components/become-provider/ServerForm.tsx @@ -73,7 +73,7 @@ export const ServerForm: React.FC = ({ currentServerNumber, onCo if (currentServerNumber === 0 || !providerProcess?.storeInformation) { return { hostname: "", - authType: "password", + authType: "file", username: "root", port: 22 }; @@ -91,7 +91,7 @@ export const ServerForm: React.FC = ({ currentServerNumber, onCo const form = useForm({ resolver: zodResolver(accountFormSchema), - defaultValues: editMode ? controlMachine?.access : (getDefaultValues() as any) + defaultValues: editMode ? { ...controlMachine?.access, username: "root" } : (getDefaultValues() as any) }); useEffect(() => { @@ -278,9 +278,9 @@ export const ServerForm: React.FC = ({ currentServerNumber, onCo name="username" render={({ field }) => ( - Username + SSH Username - + The username must be "root" for proper setup. @@ -300,12 +300,12 @@ export const ServerForm: React.FC = ({ currentServerNumber, onCo onValueChange={value => form.setValue("authType", value as "password" | "file")} > - - Password - File + + Password + = ({ currentServerNumber, onCo name="file" render={({ field }) => ( - Password File + SSH Private Key File fileChange(e, field)} /> @@ -356,6 +356,27 @@ export const ServerForm: React.FC = ({ currentServerNumber, onCo
+ {currentServerNumber === 0 && !editMode && ( +
+ ( + + + + + + Apply this config to all nodes? + + + )} + /> +
+ )} +
+
+
@@ -364,7 +385,7 @@ export const ServerForm: React.FC = ({ currentServerNumber, onCo
- {currentServerNumber === 0 && !editMode && ( -
-
-

Heads up!

-

You can apply information from Control Plane 1 to all remaining nodes by checking the option below.

-
- ( - - - - - - Yes - - - )} - /> - -
-
-
- )}
diff --git a/apps/provider-console/src/components/become-provider/WalletImport.tsx b/apps/provider-console/src/components/become-provider/WalletImport.tsx index 96d72b418..3ad12d5f8 100644 --- a/apps/provider-console/src/components/become-provider/WalletImport.tsx +++ b/apps/provider-console/src/components/become-provider/WalletImport.tsx @@ -5,7 +5,6 @@ import { Button, Form, FormControl, - FormDescription, FormField, FormItem, FormLabel, @@ -16,7 +15,7 @@ import { Textarea } from "@akashnetwork/ui/components"; import { zodResolver } from "@hookform/resolvers/zod"; -import { Home } from "iconoir-react"; +import { Check, Copy, QuestionMark, RefreshDouble } from "iconoir-react"; import { useAtom } from "jotai"; import { useRouter } from "next/router"; import { z } from "zod"; @@ -95,39 +94,62 @@ export const WalletImport: React.FC = ({ onComplete }) => { resolver: zodResolver(seedFormSchema) }); + const [copiedCommand, setCopiedCommand] = useState(false); + const [copiedPassphrase, setCopiedPassphrase] = useState(false); + + const handleCopy = (text: string, setCopied: React.Dispatch>) => { + navigator.clipboard.writeText(text); + setCopied(true); + setTimeout(() => setCopied(false), 5000); + }; + const submitData = async (data: AppearanceFormValues) => { setMode(data.walletMode); }; - const submitForm = async (data: SeedFormValues) => { + const createFinalRequest = (wallet: any) => ({ + wallet, + nodes: providerProcess.machines.map(machine => ({ + hostname: machine.access.hostname, + port: machine.access.port, + username: machine.access.username, + keyfile: machine.access.file, + password: machine.access.password, + install_gpu_drivers: machine.systemInfo.gpu.count > 0 + })), + provider: { + attributes: providerProcess.attributes, + pricing: providerProcess.pricing, + config: providerProcess.config + } + }); + + const submitForm = async (data: SeedFormValues | null = null) => { setIsLoading(true); setError(null); try { if (providerProcess.machines && providerProcess.machines.length > 0) { - const publicKey = providerProcess.machines[0].systemInfo.public_key; const keyId = providerProcess.machines[0].systemInfo.key_id; - const encryptedSeedPhrase = await encrypt(data.seedPhrase, publicKey); + let finalRequest: any; + if (mode === "seed") { + const publicKey = providerProcess.machines[0].systemInfo.public_key; + const encryptedSeedPhrase = await encrypt(data?.seedPhrase || "", publicKey); - const finalRequest = { - wallet: { + const wallet = { key_id: keyId, - wallet_phrase: encryptedSeedPhrase - }, - nodes: providerProcess.machines.map(machine => ({ - hostname: machine.access.hostname, - port: machine.access.port, - username: machine.access.username, - keyfile: machine.access.file, - password: machine.access.password, - install_gpu_drivers: machine.systemInfo.gpu.count > 0 ? true : false - })), - provider: { - attributes: providerProcess.attributes, - pricing: providerProcess.pricing, - config: providerProcess.config - } - }; + wallet_phrase: encryptedSeedPhrase, + import_mode: "auto" + }; + + finalRequest = createFinalRequest(wallet); + } else { + const wallet = { + key_id: keyId, + import_mode: "manual" + }; + finalRequest = createFinalRequest(wallet); + } const response: any = await restClient.post("/build-provider", finalRequest, { headers: { "Content-Type": "application/json" } }); @@ -172,7 +194,9 @@ export const WalletImport: React.FC = ({ onComplete }) => {

Import Wallet

-

Provider needs to import their wallet into their control machine in order to become provider.

+

+ A wallet is necessary in order to bid on workloads and to receive funds from deployments (tenants/users). +

@@ -183,8 +207,6 @@ export const WalletImport: React.FC = ({ onComplete }) => { name="walletMode" render={({ field }) => ( - Wallet Mode - Choose which mode do you want to use to import wallet @@ -194,10 +216,15 @@ export const WalletImport: React.FC = ({ onComplete }) => {
-
- -

Seed Phrase Mode

-

Provider Console will auto import using secure end-to-end encryption. Seed Phrase is Required.

+
+ +

Auto Import

+

+ Console will auto import your wallet into your control node of the provider. Please have wallet seed phrase handy to enter + in the next screen. +

@@ -208,12 +235,19 @@ export const WalletImport: React.FC = ({ onComplete }) => { -
-
-
- -

Manual Mode

-

You need to login to control machine and follow the instruction to import wallet. Seed Phrase is not Required.

+
+
+
+ +

Manual Import

+

+ You will need to manually import your wallet into your control node of the provider. Please follow the instruction in the + next screen. +

@@ -231,6 +265,7 @@ export const WalletImport: React.FC = ({ onComplete }) => {
+
-
- {error && ( -
-

{error || "An error occurred during wallet import."}

-
- )} + +
+ {error && ( +
+

{error || "An error occurred during wallet import."}

+
+ )}
@@ -297,8 +331,8 @@ export const WalletImport: React.FC = ({ onComplete }) => { {mode === "manual" && (
-

Manual Mode - Import Wallet

-

Follow these instructions to manually import your wallet on your machine.

+

Manual Import

+

Follow these instructions to manually import your wallet on the control node of your provider.

@@ -306,36 +340,68 @@ export const WalletImport: React.FC = ({ onComplete }) => {

Instructions:

    -
  1. Open a terminal on your machine.
  2. -
  3. Navigate to your control machine's directory root.
  4. +
  5. Open a Terminal: On your computer, open the terminal app.
  6. - Run the following command to import your wallet: -
    - ~/bin/provider-services --keyring-backend file keys add wallet_name --recover + Go to your control node's root directory +
    + cd ~ +
  7. - Run the following command to import your wallet: -
    - echo passphrase > ~/.praetor/wallet_phrase_password.txt + Run This Command: Copy and paste the following command into the terminal, then press Enter: +
    + ~/bin/provider-services keys add provider --recover --keyring-backend file +
  8. +
  9. + Enter Your Wallet Details: +
      +
    1. + First, it will ask for your 12 or 24-word mnemonic phrase.
      + Type or paste the seed phrase for the wallet you want to import and press Enter.
      + Next, it will ask for your wallet passphrase.{" "} +
    2. +
    3. + Copy and paste the passphrase below and press Enter: +
      + {providerProcess?.machines[0]?.systemInfo?.key_id} + +
      +
    4. +
    +
-

Replace "/path/to/your/keyfile" with the actual path to your keyfile.

-

You will be prompted to enter your keyfile password. Enter it carefully.

-
- +
diff --git a/apps/provider-console/src/components/dashboard/FinanceCard.tsx b/apps/provider-console/src/components/dashboard/FinanceCard.tsx index 68d6454a5..6ad0c4e26 100644 --- a/apps/provider-console/src/components/dashboard/FinanceCard.tsx +++ b/apps/provider-console/src/components/dashboard/FinanceCard.tsx @@ -1,5 +1,5 @@ import { useMemo } from "react"; -import { Card, CardContent, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@akashnetwork/ui/components"; +import { Card, CardContent, Tooltip, TooltipContent, TooltipTrigger } from "@akashnetwork/ui/components"; import { cn } from "@akashnetwork/ui/utils"; interface PercentChangeProps { @@ -28,13 +28,13 @@ const PercentChange: React.FC = ({ currentPrice, previousPri }, [currentPrice, previousPrice]); const isZero = currentPrice === null || previousPrice === null || previousPrice === 0 || percentageChange === 0; - const value = percentageChange !== 0 ? formattedChange : 0; + const value = percentageChange !== 0 ? `${formattedChange}%` : ""; const prefix = !isZero && percentageChange > 0 ? "+" : "-"; return ( 0, "text-red-500": percentageChange < 0 })}> {prefix} - {value}% + {value} ); }; @@ -48,16 +48,14 @@ export const FinanceCard: React.FC = ({ title, subtitle, curre
{subtitle}
{title}
- - - - - - -

{message}

-
-
-
+ + + + + +

{message}

+
+
diff --git a/apps/provider-console/src/components/home/HomeContainer.tsx b/apps/provider-console/src/components/home/HomeContainer.tsx index 8f9ba9105..6301e2d6a 100644 --- a/apps/provider-console/src/components/home/HomeContainer.tsx +++ b/apps/provider-console/src/components/home/HomeContainer.tsx @@ -6,7 +6,7 @@ import { useRouter } from "next/router"; import { useWallet } from "@src/context/WalletProvider"; import { useProviderActions } from "@src/queries/useProviderQuery"; import { Layout } from "../layout/Layout"; -import { ProviderActionList } from "../shared/ProviderActionList"; +import { ActivityLogList } from "../shared/ActivityLogList"; import { NotAProvider } from "./NotAProvider"; import { WalletNotConnected } from "./WalletNotConnected"; @@ -23,7 +23,7 @@ export const HomeContainer: React.FC = () => { }, [isWalletConnected, isProvider, isOnline, providerActions, router]); return ( - +
{(!isProviderStatusFetched || isLoading) && isWalletConnected ? ( @@ -36,7 +36,7 @@ export const HomeContainer: React.FC = () => { {isWalletConnected && !isProvider && (!providerActions || providerActions.length === 0) && } {isWalletConnected && !isProvider && providerActions && providerActions.length > 0 && (
- +
)} diff --git a/apps/provider-console/src/components/icons/AkashConsoleLogo.tsx b/apps/provider-console/src/components/icons/AkashConsoleLogo.tsx index 773b7db43..9f0d65227 100644 --- a/apps/provider-console/src/components/icons/AkashConsoleLogo.tsx +++ b/apps/provider-console/src/components/icons/AkashConsoleLogo.tsx @@ -122,64 +122,23 @@ export const AkashConsoleLogoLight = ({ className = "" }: { className?: string } export const AkashConsoleBetaLogoDark = ({ className = "" }: { className?: string }) => { return ( - - - - + + + + + - - - - - - - - - - - - - - - + + + @@ -188,64 +147,23 @@ export const AkashConsoleBetaLogoDark = ({ className = "" }: { className?: strin export const AkashConsoleBetaLogoLight = ({ className = "" }: { className?: string }) => { return ( - - - - + + + + + - - - - - - - - - - - - - - - + + + diff --git a/apps/provider-console/src/components/layout/ControlMachineStatus.tsx b/apps/provider-console/src/components/layout/ControlMachineStatus.tsx new file mode 100644 index 000000000..341b07396 --- /dev/null +++ b/apps/provider-console/src/components/layout/ControlMachineStatus.tsx @@ -0,0 +1,32 @@ +import { Button, Spinner } from "@akashnetwork/ui/components"; + +import { useControlMachine } from "@src/context/ControlMachineProvider"; + +export const ControlMachineStatus = () => { + const { activeControlMachine, openControlMachineDrawer, controlMachineLoading } = useControlMachine(); + + return ( +
+ Control Node: + {activeControlMachine ? ( + Connected + ) : ( + + )} +
+ ); +}; diff --git a/apps/provider-console/src/components/layout/Layout.tsx b/apps/provider-console/src/components/layout/Layout.tsx index fb7cb6bf5..af17c2b45 100644 --- a/apps/provider-console/src/components/layout/Layout.tsx +++ b/apps/provider-console/src/components/layout/Layout.tsx @@ -5,6 +5,7 @@ import { useMediaQuery, useTheme as useMuiTheme } from "@mui/material"; import { accountBarHeight } from "@src/utils/constants"; import { cn } from "@src/utils/styleUtils"; +import { LinearLoadingSkeleton } from "../shared/LinearLoadingSkeleton"; import { Nav } from "./Nav"; import { Sidebar } from "./Sidebar"; @@ -40,7 +41,7 @@ export const Layout: React.FC = ({ children, isLoading, isUsingSettings, ); }; -const LayoutApp: React.FC = ({ children, disableContainer, containerClassName = "" }) => { +const LayoutApp: React.FC = ({ children, isLoading, disableContainer, containerClassName = "" }) => { const muiTheme = useMuiTheme(); const [isNavOpen, setIsNavOpen] = useState(true); const [isMobileOpen, setIsMobileOpen] = useState(false); @@ -68,6 +69,7 @@ const LayoutApp: React.FC = ({ children, disableContainer, containerClass setIsMobileOpen(!isMobileOpen); }; + console.log(isLoading); return ( <>
@@ -82,6 +84,7 @@ const LayoutApp: React.FC = ({ children, disableContainer, containerClass ["md:ml-[57px]"]: !isNavOpen })} > + {isLoading !== undefined && }
{children}
diff --git a/apps/provider-console/src/components/layout/ProviderStatus.tsx b/apps/provider-console/src/components/layout/ProviderStatus.tsx new file mode 100644 index 000000000..4dd68f249 --- /dev/null +++ b/apps/provider-console/src/components/layout/ProviderStatus.tsx @@ -0,0 +1,46 @@ +import { Button } from "@akashnetwork/ui/components"; +import { useRouter } from "next/router"; + +import { useSelectedChain } from "@src/context/CustomChainProvider"; +import { useWallet } from "@src/context/WalletProvider"; +import { useProviderDetails } from "@src/queries/useProviderQuery"; + +export const ProviderStatus: React.FC = () => { + const router = useRouter(); + const { isOnline } = useWallet(); + const { address } = useSelectedChain(); + const { data: providerDetails } = useProviderDetails(address); + + const routeToRemedies = () => { + if (!isOnline) { + router.push("/remedies"); + } + }; + + return ( + <> +
+ Status: + + {isOnline ? Online : Offline} + +
+
+ Audited:{" "} + {providerDetails?.isAudited ? ( + Yes + ) : ( + + )} +
+ + ); +}; diff --git a/apps/provider-console/src/components/layout/Sidebar.tsx b/apps/provider-console/src/components/layout/Sidebar.tsx index 809af7698..3bf98104e 100644 --- a/apps/provider-console/src/components/layout/Sidebar.tsx +++ b/apps/provider-console/src/components/layout/Sidebar.tsx @@ -1,22 +1,37 @@ "use client"; import React, { ReactNode, useState } from "react"; -import { Button, buttonVariants, Spinner } from "@akashnetwork/ui/components"; +import { Button, buttonVariants, Separator } 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 { Calculator, ClipboardCheck, Cloud, DatabaseCheck, Discord, Github, ListSelect, Menu, MenuScale, Rocket, Settings, X as TwitterX, Youtube } from "iconoir-react"; +import { + ClipboardCheck, + Cloud, + DatabaseCheck, + Discord, + Dollar, + Github, + ListSelect, + Menu, + MenuScale, + Rocket, + Settings, + X as TwitterX, + Youtube +} from "iconoir-react"; import { Home, OpenInWindow } from "iconoir-react"; import getConfig from "next/config"; import Image from "next/image"; import Link from "next/link"; -import { useControlMachine } from "@src/context/ControlMachineProvider"; import { useWallet } from "@src/context/WalletProvider"; import { ISidebarGroupMenu } from "@src/types"; import { closedDrawerWidth, drawerWidth } from "@src/utils/constants"; import { cn } from "@src/utils/styleUtils"; import { UrlService } from "@src/utils/urlUtils"; +import { ControlMachineStatus } from "./ControlMachineStatus"; import { ModeToggle } from "./ModeToggle"; +import { ProviderStatus } from "./ProviderStatus"; import { SidebarGroupMenu } from "./SidebarGroupMenu"; const { publicRuntimeConfig } = getConfig(); @@ -31,13 +46,11 @@ type Props = { export const Sidebar: React.FC = ({ isMobileOpen, handleDrawerToggle, isNavOpen, onOpenMenuClick }) => { const [isHovering, setIsHovering] = useState(false); - const { isProvider, isOnline } = useWallet(); + const { isProvider, isOnline, isProviderStatusFetched, isProviderOnlineStatusFetched } = useWallet(); const _isNavOpen = isNavOpen || isHovering; const muiTheme = useMuiTheme(); const smallScreen = useMediaQuery(muiTheme.breakpoints.down("md")); - const { activeControlMachine, openControlMachineDrawer, controlMachineLoading } = useControlMachine(); - const routeGroups: ISidebarGroupMenu[] = [ { hasDivider: false, @@ -55,15 +68,15 @@ export const Sidebar: React.FC = ({ isMobileOpen, handleDrawerToggle, isN activeRoutes: [UrlService.deployments()] }, { - title: "Actions", + title: "Activity Logs", icon: props => , - url: UrlService.actions(), - activeRoutes: [UrlService.actions()], + url: UrlService.activityLogs(), + activeRoutes: [UrlService.activityLogs()], disabled: false }, { title: "Pricing", - icon: props => , + icon: props => , url: UrlService.pricing(), activeRoutes: [UrlService.pricing()], disabled: false @@ -151,7 +164,7 @@ export const Sidebar: React.FC = ({ isMobileOpen, handleDrawerToggle, isN className="border-muted-foreground/20 bg-popover dark:bg-background box-border flex h-full flex-shrink-0 flex-col items-center justify-between overflow-y-auto overflow-x-hidden border-r-[1px] transition-[width] duration-300 ease-in-out md:h-[calc(100%-57px)]" >
- {(!isProvider || !isOnline) && ( + {(isProviderStatusFetched || isProviderOnlineStatusFetched) && (!isProvider || !isOnline) && ( = ({ isMobileOpen, handleDrawerToggle, isN {_isNavOpen && (
{/* */} - {controlMachineLoading ? ( -
-
- Machine: -
- -
Connecting...
-
-
-
- ) : activeControlMachine ? ( -
-
- Machine: -
-
- {activeControlMachine.access.hostname} -
-
-
- ) : ( -
-
- Machine: -
-
-
Not Connected
-
-
-
- )} - +
+ + +
+
= ({ actionId }) => { +export const ActivityLogDetails: React.FC<{ actionId: string | null }> = ({ actionId }) => { const [openAccordions, setOpenAccordions] = useState([]); const { data: actionDetails, isLoading } = useProviderActionStatus(actionId); diff --git a/apps/provider-console/src/components/shared/ProviderActionList.tsx b/apps/provider-console/src/components/shared/ActivityLogList.tsx similarity index 81% rename from apps/provider-console/src/components/shared/ProviderActionList.tsx rename to apps/provider-console/src/components/shared/ActivityLogList.tsx index ced0c757b..8a753c5b1 100644 --- a/apps/provider-console/src/components/shared/ProviderActionList.tsx +++ b/apps/provider-console/src/components/shared/ActivityLogList.tsx @@ -1,4 +1,5 @@ import React, { useCallback } from "react"; +import { Separator } from "@akashnetwork/ui/components"; import { CheckCircle, Play, XmarkCircle } from "iconoir-react"; import { useRouter } from "next/router"; @@ -10,7 +11,7 @@ interface ProviderAction { end_time?: string; } -interface ProviderActionListProps { +interface ActivityLogsListProps { actions: ProviderAction[]; } @@ -31,7 +32,7 @@ const StatusIcon: React.FC = ({ status }) => { } }; -export const ProviderActionList: React.FC = ({ actions }) => { +export const ActivityLogList: React.FC = ({ actions }) => { const router = useRouter(); const formatDate = useCallback((dateString: string) => { @@ -60,14 +61,21 @@ export const ProviderActionList: React.FC = ({ actions }; return ( -
+
+
+
Action
+
Duration
+
Timestamp
+
Status
+
+
    {actions.length > 0 ? ( actions.map(action => (
  • handleRowClick(action.id)}> -
    +
    -

    {action.name}

    +

    {action.name}

    {calculateTimeLapse(action.start_time, action.end_time)}

    diff --git a/apps/provider-console/src/pages/actions/[id]/index.tsx b/apps/provider-console/src/pages/activity-logs/[id]/index.tsx similarity index 67% rename from apps/provider-console/src/pages/actions/[id]/index.tsx rename to apps/provider-console/src/pages/activity-logs/[id]/index.tsx index 4054d4ed6..084dc508d 100644 --- a/apps/provider-console/src/pages/actions/[id]/index.tsx +++ b/apps/provider-console/src/pages/activity-logs/[id]/index.tsx @@ -3,13 +3,13 @@ import React from "react"; import { Layout } from "@src/components/layout/Layout"; -import { ProviderActionDetails } from "@src/components/shared/ProviderActionDetails"; +import { ActivityLogDetails } from "@src/components/shared/ActivityLogDetails"; type Props = { id: string | null; }; -const ActionDetailsPage: React.FC = ({ id }) => { +const ActivityLogDetailsPage: React.FC = ({ id }) => { if (!id) { return ( @@ -21,13 +21,13 @@ const ActionDetailsPage: React.FC = ({ id }) => { return (
    - +
    ); }; -export default ActionDetailsPage; +export default ActivityLogDetailsPage; export async function getServerSideProps({ params }) { return { diff --git a/apps/provider-console/src/pages/actions/index.tsx b/apps/provider-console/src/pages/activity-logs/index.tsx similarity index 70% rename from apps/provider-console/src/pages/actions/index.tsx rename to apps/provider-console/src/pages/activity-logs/index.tsx index cc3a5038f..edfe4c2c3 100644 --- a/apps/provider-console/src/pages/actions/index.tsx +++ b/apps/provider-console/src/pages/activity-logs/index.tsx @@ -1,23 +1,23 @@ "use client"; import { Layout } from "@src/components/layout/Layout"; -import { ProviderActionList } from "@src/components/shared/ProviderActionList"; +import { ActivityLogList } from "@src/components/shared/ActivityLogList"; import { Title } from "@src/components/shared/Title"; import { useProviderActions } from "@src/queries/useProviderQuery"; -const ActionsList: React.FC = () => { +const ActivityLogs: React.FC = () => { const { data: actions } = useProviderActions(); return (
    - User Actions + Activity Logs
    - +
    @@ -25,4 +25,4 @@ const ActionsList: React.FC = () => { ); }; -export default ActionsList; +export default ActivityLogs; diff --git a/apps/provider-console/src/pages/dashboard/index.tsx b/apps/provider-console/src/pages/dashboard/index.tsx index b1ae709a0..889e79f9a 100644 --- a/apps/provider-console/src/pages/dashboard/index.tsx +++ b/apps/provider-console/src/pages/dashboard/index.tsx @@ -1,20 +1,18 @@ "use client"; -import React, { useCallback, useMemo } from "react"; -import { Button, Separator, Spinner } from "@akashnetwork/ui/components"; -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@akashnetwork/ui/components"; -import { ShieldCheck, WarningTriangle } from "iconoir-react"; +import React, { useMemo } from "react"; +import { Separator, Spinner } from "@akashnetwork/ui/components"; +import { WarningTriangle } from "iconoir-react"; import Link from "next/link"; import { DashboardCardSkeleton } from "@src/components/dashboard/DashboardCardSkeleton"; import { FinanceCard } from "@src/components/dashboard/FinanceCard"; import { ResourceCards } from "@src/components/dashboard/ResourcesCard"; import { Layout } from "@src/components/layout/Layout"; -import { ProviderActionList } from "@src/components/shared/ProviderActionList"; +import { ActivityLogList } from "@src/components/shared/ActivityLogList"; import { Title } from "@src/components/shared/Title"; import { withAuth } from "@src/components/shared/withAuth"; import { useSelectedChain } from "@src/context/CustomChainProvider"; import { useWallet } from "@src/context/WalletProvider"; -import { useAKTData } from "@src/queries"; import { useProviderActions, useProviderDashboard, useProviderDetails } from "@src/queries/useProviderQuery"; import { formatUUsd } from "@src/utils/formatUsd"; @@ -33,51 +31,12 @@ const OfflineWarningBanner: React.FC = () => (
    ); -const ProviderStatusIndicators: React.FC<{ - isOnline: boolean; - isAudited: boolean; - aktPrice: string | null; -}> = ({ isOnline, isAudited, aktPrice }) => { - const handleAktPriceClick = useCallback(() => { - window.open("https://www.coingecko.com/en/coins/akash-network", "_blank"); - }, []); - - return ( - <> -
    - - - -
    - - -

    {isOnline ? "Provider is online" : "Provider is offline"}

    -
    - - -
    -
    -
    - - {isAudited ? "Audited" : "Not Audited"} -
    -
    -
    - -
    - - ); -}; - const Dashboard: React.FC = () => { - const { data: aktData }: any = useAKTData(); - const { address }: any = useSelectedChain(); - const { isOnline } = useWallet(); + const { address } = useSelectedChain(); + const { isOnline, isProviderOnlineStatusFetched } = useWallet(); - const { data: providerDetails, isLoading: isLoadingProviderDetails }: any = useProviderDetails(address); - const { data: providerDashboard, isLoading: isLoadingProviderDashboard }: any = useProviderDashboard(address); + const { data: providerDetails, isLoading: isLoadingProviderDetails } = useProviderDetails(address); + const { data: providerDashboard, isLoading: isLoadingProviderDashboard } = useProviderDashboard(address); const { data: providerActions, isLoading: isLoadingProviderActions } = useProviderActions(); const summaryCards = useMemo( @@ -85,30 +44,30 @@ const Dashboard: React.FC = () => { <> @@ -117,18 +76,17 @@ const Dashboard: React.FC = () => { ); return ( - + {providerDetails && !isOnline && }
    Dashboard
    - {providerDetails && }
    -
    +
    - Provider Summary + Earnings and Leases {isLoadingProviderDashboard && }
    @@ -146,7 +104,7 @@ const Dashboard: React.FC = () => {
    -
    Resources Leased Summary
    +
    Resources
    {isLoadingProviderDetails ? (
    @@ -164,8 +122,8 @@ const Dashboard: React.FC = () => {
    -
    Recent Provider Actions
    - {isLoadingProviderActions ? : } +
    Activity Logs
    + {isLoadingProviderActions ? : }
    diff --git a/apps/provider-console/src/queries/useProviderQuery.ts b/apps/provider-console/src/queries/useProviderQuery.ts index 6b68b7b27..7f6a42166 100644 --- a/apps/provider-console/src/queries/useProviderQuery.ts +++ b/apps/provider-console/src/queries/useProviderQuery.ts @@ -1,7 +1,7 @@ import { useQuery } from "react-query"; import { ControlMachineWithAddress } from "@src/types/controlMachine"; -import { PersistentStorageResponse, ProviderDetails } from "@src/types/provider"; +import { PersistentStorageResponse, ProviderDashoard, ProviderDetails } from "@src/types/provider"; import consoleClient from "@src/utils/consoleClient"; import { findTotalAmountSpentOnLeases, totalDeploymentCost, totalDeploymentTimeLeft } from "@src/utils/deploymentUtils"; import restClient from "@src/utils/restClient"; @@ -70,8 +70,8 @@ export const useProviderDetails = (address: string | undefined) => { }); }; -export const useProviderDashboard = (address: string) => { - return useQuery({ +export const useProviderDashboard = (address: string | undefined) => { + return useQuery({ queryKey: ["providerDashboard", address], queryFn: () => consoleClient.get(`/internal/provider-dashboard/${address}`), refetchOnWindowFocus: false, diff --git a/apps/provider-console/src/utils/formatUsd.ts b/apps/provider-console/src/utils/formatUsd.ts index 768caf56d..0c36c0a4a 100644 --- a/apps/provider-console/src/utils/formatUsd.ts +++ b/apps/provider-console/src/utils/formatUsd.ts @@ -4,7 +4,9 @@ * @param decimals The number of decimal places to show (default: 2) * @returns Formatted USD string */ -export function formatUUsd(amount: number, decimals: number = 2): string { +export function formatUUsd(amount: number | undefined, decimals: number = 2): string { + if (!amount) return "$0"; + const dollars = amount / 1000000; if (dollars >= 1000000) { diff --git a/apps/provider-console/src/utils/urlUtils.ts b/apps/provider-console/src/utils/urlUtils.ts index 60fe9f2d4..a8bde30ea 100644 --- a/apps/provider-console/src/utils/urlUtils.ts +++ b/apps/provider-console/src/utils/urlUtils.ts @@ -22,7 +22,7 @@ export class UrlService { static getStarted = () => "/get-started"; static privacyPolicy = () => "/privacy-policy"; static termsOfService = () => "/terms-of-service"; - static actions = () => "/actions"; + static activityLogs = () => "/activity-logs"; static pricing = () => "/pricing"; static persistentStorage = () => "/persistent-storage"; }