Skip to content

Commit

Permalink
feat(provider): provider attributes and actions (#451)
Browse files Browse the repository at this point in the history
* added server-access and server-form files

* added wallet import page - wip, fixed issue with file upload in server access forms

* added become-provider steps ui

* server access step added with state support

* added provider config and provider attribute screen

* added provider process with hoc to prevent access to pages

* added progress for becoming prvider for final stage

* Code clean up and added navigation logic to homecontainer

* package lock updated

* more cleanup and remove general warnings

* removed unused npm package

* fix minor error on api

* Added dashboard and actions page

* change status api endpoint

* added stat line and pie charts

* Added console apis to get dashboard data and show appropriate details

* fixed actions and changed home component

* token varification and refresh token fix

* changed wallet connect from wallet status to wallet provider

* fixed issue in loading provider status

* fixed home loading issue

* fixed refresh token, added disabled menu items

* fixed build process

* feat(provider): added sentry and docker

* fix(provider): fixed wallet switching and getting status

* feat(provider): reduced number of events in dashboard

* feat(provider): added docker compose changes for provider-console

* feat(provider): added deployments and deployment detail page

* fix(provider): change hours to seconds for calculation purpose)

* feat(provider): added auth for deployments and deployment details page

* feat(provider): added env and removed settingsprovider

* fix(provider): fix lint errors and removed console.logs

* fix(provider): become-provider looped, fixed it

* fix(provider): router and reset process fixed

* fix(provider): removed Get Started button for now

* fix(provider): removed unused import in nav

* fix(provider): change functions to react fc component

* fix(provider): fix lint issues

* fix(provider): change functions to react fc component

* fix(provider): added docker build and fix build related issues

* feat(provider): control machine edit, add from sidebar

* feat(provider): added attributes screen

* fix(provider): control machine auto connect on page load

* fix(provider): fix loading not showing while connecting provider control machine

* fix(provider): close drawer on successfull connection

* feat(provider): change favicon to akash favicon

* feat(provider): provider add, edit and remove and show acitons list page

* fix(provider): fix url when provider process finish

* fix(provider): merge issues with main

* fix(provider): changed to useQuery and fix lint

* chore(provider): removed comment
  • Loading branch information
jigar-arc10 authored Dec 2, 2024
1 parent c676fd2 commit 6dfaf3b
Show file tree
Hide file tree
Showing 11 changed files with 169 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import React from "react";
import { Controller, SubmitHandler, useFieldArray, useForm } from "react-hook-form";
import {
Alert,
AlertDescription,
AlertTitle,
Button,
Form,
FormControl,
Expand All @@ -14,21 +17,25 @@ 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";
import { z } from "zod";

import providerProcessStore from "@src/store/providerProcessStore";
import { useControlMachine } from "@src/context/ControlMachineProvider";
import providerProcessStore, { ProviderAttribute } from "@src/store/providerProcessStore";
import restClient from "@src/utils/restClient";
import { sanitizeMachineAccess } from "@src/utils/sanityUtils";
import { providerAttributesFormValuesSchema } from "../../types/providerAttributes";
import { ResetProviderForm } from "./ResetProviderProcess";

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

interface ProviderAttributesProps {
onComplete: () => void;
existingAttributes?: ProviderAttribute[];
editMode?: boolean;
onComplete?: () => void;
}

const providerFormSchema = z.object({
Expand All @@ -43,40 +50,68 @@ const providerFormSchema = z.object({

type ProviderFormValues = z.infer<typeof providerFormSchema>;

export const ProviderAttributes: React.FC<ProviderAttributesProps> = ({ onComplete }) => {
export const ProviderAttributes: React.FunctionComponent<ProviderAttributesProps> = ({ onComplete, existingAttributes, editMode }) => {
const [providerPricing, setProviderPricing] = useAtom(providerProcessStore.providerProcessAtom);
const form = useForm<ProviderFormValues>({
resolver: zodResolver(providerFormSchema),
defaultValues: {
attributes: [{ key: "", value: "", customKey: "" }]
attributes: existingAttributes
? existingAttributes.map(attr => ({
key: attributeKeys.includes(attr.key) ? attr.key : "unknown-attributes",
value: attr.value,
customKey: attributeKeys.includes(attr.key) ? "" : attr.key
}))
: [{ key: "", value: "", customKey: "" }]
}
});

const { control } = form;

const { fields, append, remove } = useFieldArray({
control,
name: "attributes"
});

const { activeControlMachine } = useControlMachine();

const [showSuccess, setShowSuccess] = React.useState(false);

const updateProviderAttributesAndProceed: SubmitHandler<ProviderFormValues> = async data => {
const updatedProviderPricing = {
...providerPricing,
attributes: data.attributes.map(attr => ({
...attr,
customKey: attr.customKey || ""
}))
};
setProviderPricing(updatedProviderPricing);
onComplete();
if (!editMode) {
const updatedProviderPricing = {
...providerPricing,
attributes: data.attributes.map(attr => ({
key: attr.key === "unknown-attributes" ? attr.customKey || "" : attr.key || "",
value: attr.value
}))
};
setProviderPricing(updatedProviderPricing);
onComplete && onComplete();
} else {
const attributes = data.attributes.map(attr => ({
key: attr.key === "unknown-attributes" ? attr.customKey || "" : attr.key || "",
value: attr.value
}));
const request = {
control_machine: sanitizeMachineAccess(activeControlMachine),
attributes
};

const response = await restClient.post(`/update-provider-attributes`, request);
if (response) {
setShowSuccess(true);
setTimeout(() => setShowSuccess(false), 10000);
}
}
};

return (
<div className="flex w-full flex-col items-center pt-10">
<div className="w-full max-w-2xl space-y-6">
<div>
<h3 className="text-xl font-bold">Provider Attributes</h3>
<p className="text-muted-foreground text-sm">Please enter your provider attributes.</p>
<h3 className="text-xl font-bold">{existingAttributes ? "Edit Provider Attributes" : "Provider Attributes"}</h3>
<p className="text-muted-foreground text-sm">
{existingAttributes ? "Please update your provider attributes." : "Please enter your provider attributes."}
</p>
</div>
<div>
<Separator />
Expand Down Expand Up @@ -154,16 +189,20 @@ export const ProviderAttributes: React.FC<ProviderAttributesProps> = ({ onComple
<Separator />
</div>
<div className="flex w-full justify-between">
<div className="flex justify-start">
<ResetProviderForm />
</div>
<div className="flex justify-start">{!editMode && <ResetProviderForm />}</div>
<div className="flex justify-end">
<Button type="submit">Next</Button>
<Button type="submit">{editMode ? "Update Attributes" : "Next"}</Button>
</div>
</div>
</form>
</Form>
</div>
{showSuccess && (
<Alert>
<AlertTitle>Success</AlertTitle>
<AlertDescription>Provider attributes updated successfully</AlertDescription>
</Alert>
)}
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ export const WalletImport: React.FC<WalletImportProps> = ({ onComplete }) => {
};
await setControlMachine(machineWithAddress);
resetProviderProcess();
router.push(`/action?id=${response.action_id}`);
router.push(`/actions/${response.action_id}`);
} else {
throw new Error("Invalid response from server");
}
Expand Down
12 changes: 6 additions & 6 deletions apps/provider-console/src/components/layout/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ export const Sidebar: React.FC<Props> = ({ isMobileOpen, handleDrawerToggle, isN
{
title: "Actions",
icon: props => <ClipboardCheck {...props} />,
url: "#",
activeRoutes: ["#"],
disabled: true
url: UrlService.actions(),
activeRoutes: [UrlService.actions()],
disabled: false
},
{
title: "Pricing",
Expand All @@ -71,9 +71,9 @@ export const Sidebar: React.FC<Props> = ({ isMobileOpen, handleDrawerToggle, isN
{
title: "Attributes",
icon: props => <ListSelect {...props} />,
url: "#",
activeRoutes: ["#"],
disabled: true
url: UrlService.attributes(),
activeRoutes: [UrlService.attributes()],
disabled: false
},
{
title: "Settings",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ export const ProviderActionDetails: React.FC<{ actionId: string | null }> = ({ a
<div className="rounded-md border">
{actionDetails?.tasks.map((task, index) => (
<div key={index}>
<div className="flex cursor-pointer items-center justify-between p-4" onClick={() => toggleAccordion(index)}>
<div
className="flex cursor-pointer items-center justify-between p-4 hover:bg-gray-50 dark:hover:bg-gray-600/50"
onClick={() => toggleAccordion(index)}
>
<div className="flex items-center">
{openAccordions[index] ? <ArrowDown className="mr-2 h-5 w-5" /> : <ArrowRight className="mr-2 h-5 w-5" />}
<span>{task.description}</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,15 @@ export const ProviderActionList: React.FC<ProviderActionListProps> = ({ actions
};

const handleRowClick = (actionId: string) => {
router.push(`/action-details/?id=${actionId}`);
router.push(`/actions/${actionId}`);
};

return (
<div className="w-full">
<ul className="divide-y divide-gray-200">
{actions.length > 0 ? (
actions.map(action => (
<li key={action.id} className="cursor-pointer py-4 hover:bg-gray-50" onClick={() => handleRowClick(action.id)}>
<li key={action.id} className="cursor-pointer py-4 hover:bg-gray-50 dark:hover:bg-gray-600/50" onClick={() => handleRowClick(action.id)}>
<div className="grid grid-cols-12 items-center gap-4">
<div className="col-span-4">
<p className="text-base font-medium">{action.name}</p>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
"use client";

import React from "react";
import { useSearchParams } from "next/navigation";

import { Layout } from "@src/components/layout/Layout";
import { ProviderActionDetails } from "@src/components/shared/ProviderActionDetails";

export default function ActionDetailsPage() {
const searchParams = useSearchParams();
const actionId = searchParams.get("id");
type Props = {
id: string | null;
};

if (!actionId) {
const ActionDetailsPage: React.FC<Props> = ({ id }) => {
if (!id) {
return (
<Layout>
<div>Error: No action ID provided</div>
Expand All @@ -21,8 +21,18 @@ export default function ActionDetailsPage() {
return (
<Layout>
<div className="container mx-auto">
<ProviderActionDetails actionId={actionId} />
<ProviderActionDetails actionId={id} />
</div>
</Layout>
);
};

export default ActionDetailsPage;

export async function getServerSideProps({ params }) {
return {
props: {
id: params?.id
}
};
}
28 changes: 28 additions & 0 deletions apps/provider-console/src/pages/actions/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"use client";

import { Layout } from "@src/components/layout/Layout";
import { ProviderActionList } from "@src/components/shared/ProviderActionList";
import { Title } from "@src/components/shared/Title";
import { useProviderActions } from "@src/queries/useProviderQuery";

const ActionsList: React.FC = () => {
const { data: actions } = useProviderActions();
return (
<Layout>
<div className="flex items-center">
<div className="w-10 flex-1">
<Title>User Actions</Title>
</div>
</div>
<div className="mt-10">
<div className="text-sm font-semibold">
<div className="items-center space-x-2">
<ProviderActionList actions={actions} />
</div>
</div>
</div>
</Layout>
);
};

export default ActionsList;
33 changes: 33 additions & 0 deletions apps/provider-console/src/pages/attributes/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from "react";
import { useQuery } from "react-query";

import { ProviderAttributes } from "@src/components/become-provider/ProviderAttributes";
import { Layout } from "@src/components/layout/Layout";
import { useSelectedChain } from "@src/context/CustomChainProvider";
import consoleClient from "@src/utils/consoleClient";

const Attributes: React.FunctionComponent = () => {
const { address } = useSelectedChain();
const { data: providerDetails, isLoading: isLoadingProviderDetails }: { data: any; isLoading: boolean } = useQuery(
"providerDetails",
() => consoleClient.get(`/v1/providers/${address}`),
{
refetchOnWindowFocus: false,
retry: 3
}
);

return (
<Layout>
{isLoadingProviderDetails ? (
<div>Loading...</div>
) : (
<div>
<ProviderAttributes existingAttributes={providerDetails.attributes} editMode={true} />
</div>
)}
</Layout>
);
};

export default Attributes;
2 changes: 1 addition & 1 deletion apps/provider-console/src/store/providerProcessStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ interface ProviderPricing {
endpointBidPrice: number
}

interface ProviderAttribute { key: string, value: string, customKey: string }
export interface ProviderAttribute { key: string, value: string }

interface ProviderProcess {
machines: MachineInformation[],
Expand Down
15 changes: 15 additions & 0 deletions apps/provider-console/src/utils/sanityUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ControlMachineWithAddress } from "@src/types/controlMachine";

export function sanitizeMachineAccess(machine: ControlMachineWithAddress | null) {
if (!machine) {
return undefined;
}
return {
hostname: machine.access.hostname,
port: machine.access.port,
username: machine.access.username,
keyfile: machine.access.file || null,
password: machine.access.password || null,
passphrase: machine.access.passphrase || null
};
}
2 changes: 2 additions & 0 deletions apps/provider-console/src/utils/urlUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ export const domainName = "https://provider.akash.network";
export class UrlService {
static home = () => "/";
static deployments = () => "/deployments";
static attributes = () => "/attributes";
static getStarted = () => "/get-started";
static privacyPolicy = () => "/privacy-policy";
static termsOfService = () => "/terms-of-service";
static actions = () => "/actions";
}

0 comments on commit 6dfaf3b

Please sign in to comment.