Skip to content

Commit 68063f1

Browse files
authored
Min AKT and minor bug fixes (#679)
* feat(provider): added popup for min akt needed for provider * fix(provider): change become-provider to create-provider * fix(provider): lint fixed * chore: changed min akt requirement for create provider screen * feat(provider): added api for updating url * chore: fix small bugs in loading pages for non-providers * chore: added control-plane info and default attributes * fix: fixed small bug * fix(provider): fixed issue with types * fix: min 5 akt
1 parent 9c6cddf commit 68063f1

File tree

13 files changed

+296
-69
lines changed

13 files changed

+296
-69
lines changed

apps/provider-console/src/components/become-provider/ProviderAttributes.tsx

Lines changed: 84 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ import {
1717
SelectContent,
1818
SelectItem,
1919
SelectTrigger,
20-
Separator
20+
Separator,
21+
Tooltip,
22+
TooltipContent,
23+
TooltipProvider,
24+
TooltipTrigger
2125
} from "@akashnetwork/ui/components";
2226
import { zodResolver } from "@hookform/resolvers/zod";
2327
import { Plus, Trash } from "iconoir-react";
@@ -33,6 +37,8 @@ import { ResetProviderForm } from "./ResetProviderProcess";
3337

3438
const attributeKeys = Object.keys(providerAttributesFormValuesSchema.shape);
3539

40+
const DEFAULT_ATTRIBUTES = ['host', 'tier'];
41+
3642
interface ProviderAttributesProps {
3743
existingAttributes?: ProviderAttribute[];
3844
editMode?: boolean;
@@ -52,17 +58,23 @@ const providerFormSchema = z.object({
5258
type ProviderFormValues = z.infer<typeof providerFormSchema>;
5359

5460
export const ProviderAttributes: React.FunctionComponent<ProviderAttributesProps> = ({ onComplete, existingAttributes, editMode }) => {
55-
const [providerPricing, setProviderPricing] = useAtom(providerProcessStore.providerProcessAtom);
61+
const [providerProcess, setProviderProcess] = useAtom(providerProcessStore.providerProcessAtom);
62+
const organizationName = providerProcess.config?.organization;
63+
5664
const form = useForm<ProviderFormValues>({
5765
resolver: zodResolver(providerFormSchema),
5866
defaultValues: {
5967
attributes: existingAttributes
6068
? existingAttributes.map(attr => ({
61-
key: attributeKeys.includes(attr.key) ? attr.key : "unknown-attributes",
62-
value: attr.value,
63-
customKey: attributeKeys.includes(attr.key) ? "" : attr.key
64-
}))
65-
: [{ key: "", value: "", customKey: "" }]
69+
key: attributeKeys.includes(attr.key) ? attr.key : "unknown-attributes",
70+
value: attr.value,
71+
customKey: attributeKeys.includes(attr.key) ? "" : attr.key
72+
}))
73+
: [
74+
{ key: "host", value: "akash", customKey: "" },
75+
{ key: "tier", value: "community", customKey: "" },
76+
{ key: "organization", value: organizationName || "", customKey: "" }
77+
]
6678
}
6779
});
6880

@@ -78,14 +90,14 @@ export const ProviderAttributes: React.FunctionComponent<ProviderAttributesProps
7890

7991
const updateProviderAttributesAndProceed: SubmitHandler<ProviderFormValues> = async data => {
8092
if (!editMode) {
81-
const updatedProviderPricing = {
82-
...providerPricing,
93+
const updatedProviderProcess = {
94+
...providerProcess,
8395
attributes: data.attributes.map(attr => ({
8496
key: attr.key === "unknown-attributes" ? attr.customKey || "" : attr.key || "",
8597
value: attr.value
8698
}))
8799
};
88-
setProviderPricing(updatedProviderPricing);
100+
setProviderProcess(updatedProviderProcess);
89101
onComplete && onComplete();
90102
} else {
91103
const attributes = data.attributes.map(attr => ({
@@ -124,30 +136,48 @@ export const ProviderAttributes: React.FunctionComponent<ProviderAttributesProps
124136
{fields.map((field, index) => {
125137
const selectedKeys = form.watch("attributes").map(attr => attr.key);
126138
const availableKeys = attributeKeys.filter(key => !selectedKeys.includes(key) || key === field.key || key === "unknown-attributes");
139+
const isDefaultAttribute = DEFAULT_ATTRIBUTES.includes(field.key);
127140

128141
return (
129142
<div key={field.id} className="mb-2 flex space-x-2">
130-
<Controller
131-
control={form.control}
132-
name={`attributes.${index}.key`}
133-
render={({ field }) => (
134-
<FormItem className="flex-1">
135-
<FormControl>
136-
<Select value={field.value} onValueChange={value => field.onChange(value)}>
137-
<SelectTrigger>{field.value || "Select Key"}</SelectTrigger>
138-
<SelectContent>
139-
{availableKeys.map(key => (
140-
<SelectItem key={key} value={key}>
141-
{key}
142-
</SelectItem>
143-
))}
144-
</SelectContent>
145-
</Select>
146-
</FormControl>
147-
<FormMessage />
148-
</FormItem>
149-
)}
150-
/>
143+
<TooltipProvider>
144+
<Controller
145+
control={form.control}
146+
name={`attributes.${index}.key`}
147+
render={({ field }) => (
148+
<FormItem className="flex-1">
149+
<FormControl>
150+
<Tooltip>
151+
<TooltipTrigger asChild>
152+
<div>
153+
<Select
154+
value={field.value}
155+
onValueChange={value => field.onChange(value)}
156+
disabled={isDefaultAttribute}
157+
>
158+
<SelectTrigger>{field.value || "Select Key"}</SelectTrigger>
159+
<SelectContent>
160+
{availableKeys.map(key => (
161+
<SelectItem key={key} value={key}>
162+
{key}
163+
</SelectItem>
164+
))}
165+
</SelectContent>
166+
</Select>
167+
</div>
168+
</TooltipTrigger>
169+
{isDefaultAttribute && (
170+
<TooltipContent>
171+
<p>This is a default attribute and cannot be modified</p>
172+
</TooltipContent>
173+
)}
174+
</Tooltip>
175+
</FormControl>
176+
<FormMessage />
177+
</FormItem>
178+
)}
179+
/>
180+
</TooltipProvider>
151181
{form.watch(`attributes.${index}.key`) === "unknown-attributes" && (
152182
<FormField
153183
control={form.control}
@@ -168,13 +198,34 @@ export const ProviderAttributes: React.FunctionComponent<ProviderAttributesProps
168198
render={({ field }) => (
169199
<FormItem className="flex-1">
170200
<FormControl>
171-
<Input placeholder="Value" {...field} />
201+
<Tooltip>
202+
<TooltipTrigger asChild>
203+
<div>
204+
<Input
205+
placeholder="Value"
206+
{...field}
207+
disabled={isDefaultAttribute}
208+
/>
209+
</div>
210+
</TooltipTrigger>
211+
{isDefaultAttribute && (
212+
<TooltipContent>
213+
<p>This is a default attribute and cannot be modified</p>
214+
</TooltipContent>
215+
)}
216+
</Tooltip>
172217
</FormControl>
173218
<FormMessage />
174219
</FormItem>
175220
)}
176221
/>
177-
<Button type="button" variant="outline" size="icon" onClick={() => remove(index)}>
222+
<Button
223+
type="button"
224+
variant="outline"
225+
size="icon"
226+
onClick={() => remove(index)}
227+
disabled={isDefaultAttribute}
228+
>
178229
<Trash className="h-4 w-4" />
179230
</Button>
180231
</div>

apps/provider-console/src/components/become-provider/ServerAccess.tsx

Lines changed: 135 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,40 @@
11
"use client";
22
import React, { useCallback, useState } from "react";
3-
import { Button, Input, Separator } from "@akashnetwork/ui/components";
3+
import { Button, Input, Popup, Separator } from "@akashnetwork/ui/components";
44

5+
import { useWallet } from "@src/context/WalletProvider";
56
import { ServerForm } from "./ServerForm";
67

78
interface ServerAccessProps {
89
onComplete: () => void;
910
}
1011

12+
interface NodeCounts {
13+
controlPlane: number;
14+
workerNodes: number;
15+
}
16+
17+
interface ServerTypeInfo {
18+
isControlPlane: boolean;
19+
nodeNumber: number;
20+
}
21+
1122
export const ServerAccess: React.FC<ServerAccessProps> = ({ onComplete }) => {
1223
const [numberOfServers, setNumberOfServers] = useState(1);
1324
const [activateServerForm, setActivateServerForm] = useState(false);
1425
const [currentServer, setCurrentServer] = useState(0);
26+
const [showBalancePopup, setShowBalancePopup] = useState(false);
27+
const [showNodeDistribution, setShowNodeDistribution] = useState(false);
28+
29+
const { walletBalances } = useWallet();
30+
const MIN_BALANCE = 5_000_000;
31+
const hasEnoughBalance = (walletBalances?.uakt || 0) >= MIN_BALANCE;
32+
33+
React.useEffect(() => {
34+
if (!hasEnoughBalance) {
35+
setShowBalancePopup(true);
36+
}
37+
}, [hasEnoughBalance]);
1538

1639
const handleServerFormSubmit = useCallback(() => {
1740
if (currentServer + 1 >= numberOfServers) {
@@ -25,9 +48,59 @@ export const ServerAccess: React.FC<ServerAccessProps> = ({ onComplete }) => {
2548
setNumberOfServers(value);
2649
}, []);
2750

51+
const calculateNodeDistribution = useCallback((totalNodes: number): NodeCounts => {
52+
if (totalNodes <= 3) {
53+
return { controlPlane: 1, workerNodes: totalNodes - 1 };
54+
}
55+
if (totalNodes <= 5) {
56+
return { controlPlane: 3, workerNodes: totalNodes - 3 };
57+
}
58+
59+
const baseControlPlane = 3;
60+
const additionalPairs = Math.floor((totalNodes - 1) / 50);
61+
const controlPlane = Math.min(baseControlPlane + additionalPairs * 2, 11); // Cap at 11 control plane nodes
62+
return { controlPlane, workerNodes: totalNodes - controlPlane };
63+
}, []);
64+
65+
const handleNextClick = useCallback(() => {
66+
if (!hasEnoughBalance) {
67+
setShowBalancePopup(true);
68+
return;
69+
}
70+
setShowNodeDistribution(true);
71+
}, [hasEnoughBalance]);
72+
73+
const handleDistributionNext = useCallback(() => {
74+
setShowNodeDistribution(false);
75+
setActivateServerForm(true);
76+
}, []);
77+
78+
const handleClosePopup = useCallback(() => {
79+
setShowBalancePopup(false);
80+
}, []);
81+
82+
const getCurrentServerType = useCallback(
83+
(serverIndex: number): ServerTypeInfo => {
84+
const { controlPlane } = calculateNodeDistribution(numberOfServers);
85+
86+
if (serverIndex < controlPlane) {
87+
return {
88+
isControlPlane: true,
89+
nodeNumber: serverIndex + 1
90+
};
91+
}
92+
93+
return {
94+
isControlPlane: false,
95+
nodeNumber: serverIndex - controlPlane + 1
96+
};
97+
},
98+
[calculateNodeDistribution, numberOfServers]
99+
);
100+
28101
return (
29102
<div className="flex flex-col items-center pt-10">
30-
{!activateServerForm ? (
103+
{!activateServerForm && !showNodeDistribution ? (
31104
<div className="space-y-6">
32105
<div className="flex items-center space-x-4">
33106
<h3 className="text-xl font-bold">Server Count</h3>
@@ -50,13 +123,71 @@ export const ServerAccess: React.FC<ServerAccessProps> = ({ onComplete }) => {
50123
<div className="flex w-full justify-between">
51124
<div className="flex justify-start"></div>
52125
<div className="flex justify-end">
53-
<Button onClick={() => setActivateServerForm(true)}>Next</Button>
126+
<Button onClick={handleNextClick}>Next</Button>
127+
</div>
128+
</div>
129+
</div>
130+
) : showNodeDistribution ? (
131+
<div className="space-y-6">
132+
<div className="flex gap-6">
133+
<div className="rounded-lg border p-6 text-center">
134+
<p className="mb-4 text-3xl font-bold">{calculateNodeDistribution(numberOfServers).controlPlane}</p>
135+
<h3 className="mb-2 text-xl font-bold">Control Plane Nodes</h3>
136+
<p className="text-sm">Manages the cluster operations & run your workloads</p>
137+
</div>
138+
<div className="rounded-lg border p-6 text-center">
139+
<p className="mb-4 text-3xl font-bold">{calculateNodeDistribution(numberOfServers).workerNodes}</p>
140+
<h3 className="mb-2 text-xl font-bold">Worker Nodes</h3>
141+
<p className="text-sm">Runs your workloads</p>
54142
</div>
55143
</div>
144+
<div className="flex w-full justify-between">
145+
<Button variant="ghost" onClick={() => setShowNodeDistribution(false)}>
146+
Back
147+
</Button>
148+
<Button onClick={handleDistributionNext}>Next</Button>
149+
</div>
56150
</div>
57151
) : (
58-
<ServerForm key={currentServer} currentServerNumber={currentServer} onComplete={handleServerFormSubmit} />
152+
<ServerForm key={currentServer} currentServerNumber={currentServer} onComplete={handleServerFormSubmit} {...getCurrentServerType(currentServer)} />
59153
)}
154+
155+
<Popup
156+
fullWidth
157+
open={showBalancePopup}
158+
variant="custom"
159+
actions={[
160+
{
161+
label: "Close",
162+
color: "primary",
163+
variant: "ghost",
164+
side: "left",
165+
onClick: handleClosePopup
166+
}
167+
]}
168+
onClose={handleClosePopup}
169+
maxWidth="xs"
170+
enableCloseOnBackdropClick
171+
title="Insufficient Balance"
172+
>
173+
<div>
174+
<div className="pb-2">
175+
<p>
176+
You need at least <strong>5 AKT</strong> to become a provider.
177+
<br />
178+
Every lease created on the Akash network requires <strong>0.5 AKT</strong> to be locked in escrow.
179+
<br />
180+
Please ensure you have enough funds to cover your resources.
181+
</p>
182+
</div>
183+
<Separator />
184+
<div>
185+
<p className="pt-2">
186+
You currently have <strong>{(walletBalances?.uakt || 0) / 1000000} AKT</strong>.
187+
</p>
188+
</div>
189+
</div>
190+
</Popup>
60191
</div>
61192
);
62193
};

0 commit comments

Comments
 (0)