From 7bf86eb57d83bbe8c30da0e7b5eec39f483384a0 Mon Sep 17 00:00:00 2001 From: amalcaraz Date: Mon, 7 Aug 2023 08:58:32 +0200 Subject: [PATCH] WIP: form validation --- package-lock.json | 14 +- package.json | 2 +- src/components/common/HiddenFileInput/cmp.tsx | 126 +++--- src/components/form/AddDomains/types.ts | 2 + src/components/form/AddSSHKeys/cmp.tsx | 91 +++-- src/components/form/AddVolume/cmp.tsx | 17 +- .../form/SelectInstanceImage/cmp.tsx | 112 ++--- .../form/SelectInstanceImage/types.ts | 1 + .../form/SelectInstanceSpecs/cmp.tsx | 246 +++++------ .../pages/dashboard/NewFunctionPage/cmp.tsx | 383 +++++++++--------- .../pages/dashboard/NewInstancePage/cmp.tsx | 350 ++++++++-------- .../pages/dashboard/NewSSHKeyPage/cmp.tsx | 90 ++-- .../pages/dashboard/NewVolumePage/cmp.tsx | 73 ++-- .../dashboard/manage/ManageInstance/cmp.tsx | 4 +- src/domain/executable.ts | 54 +-- src/domain/instance.ts | 99 +++-- src/domain/program.ts | 176 ++++---- src/domain/volume.ts | 4 +- src/helpers/schemas.ts | 119 ++++-- src/hooks/common/useForm.ts | 34 +- src/hooks/form/useAddDomains.ts | 13 +- src/hooks/form/useAddNameAndTags.ts | 3 +- src/hooks/form/useAddSSHKeys.ts | 3 +- src/hooks/form/useAddVolume.ts | 41 +- src/hooks/form/useAddVolumes.ts | 12 +- src/hooks/form/useSelectFunctionRuntime.ts | 3 +- src/hooks/pages/dashboard/useNewDomainPage.ts | 13 +- .../pages/dashboard/useNewFunctionPage.ts | 62 ++- .../pages/dashboard/useNewInstancePage.ts | 76 ++-- src/hooks/pages/dashboard/useNewSSHKeyPage.ts | 2 +- src/hooks/pages/dashboard/useNewVolumePage.ts | 6 +- 31 files changed, 1162 insertions(+), 1069 deletions(-) diff --git a/package-lock.json b/package-lock.json index f2682a10..32e269fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "name": "front-aleph-cloud", "version": "0.2.7", "dependencies": { - "@aleph-front/aleph-core": "^1.3.10", + "@aleph-front/aleph-core": "^1.3.13", "@fortawesome/fontawesome-svg-core": "^6.3.0", "@hookform/resolvers": "^3.1.1", "@types/node": "18.14.1", @@ -134,9 +134,9 @@ } }, "node_modules/@aleph-front/aleph-core": { - "version": "1.3.10", - "resolved": "https://registry.npmjs.org/@aleph-front/aleph-core/-/aleph-core-1.3.10.tgz", - "integrity": "sha512-I4FbQWZrd3/4PHYArz2DNoofiWyio57yXlFyolLlyD1/l1TREgU279DiXiNNF40BI/1migfHeO/q7ux8MhjNFg==", + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@aleph-front/aleph-core/-/aleph-core-1.3.13.tgz", + "integrity": "sha512-s3kjq4OAiVKRlS5ZlfNDlpBsdAo6D2aqA/ZMKmLPb7LHFI56iJOEYPKQiSk72AFzn2a2UBnahs9toLy+Ggv5aw==", "dependencies": { "@monaco-editor/react": "^4.4.6", "react-transition-group": "^4.4.5" @@ -10326,9 +10326,9 @@ } }, "@aleph-front/aleph-core": { - "version": "1.3.10", - "resolved": "https://registry.npmjs.org/@aleph-front/aleph-core/-/aleph-core-1.3.10.tgz", - "integrity": "sha512-I4FbQWZrd3/4PHYArz2DNoofiWyio57yXlFyolLlyD1/l1TREgU279DiXiNNF40BI/1migfHeO/q7ux8MhjNFg==", + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@aleph-front/aleph-core/-/aleph-core-1.3.13.tgz", + "integrity": "sha512-s3kjq4OAiVKRlS5ZlfNDlpBsdAo6D2aqA/ZMKmLPb7LHFI56iJOEYPKQiSk72AFzn2a2UBnahs9toLy+Ggv5aw==", "requires": { "@monaco-editor/react": "^4.4.6", "react-transition-group": "^4.4.5" diff --git a/package.json b/package.json index d0da4df6..c2d2a205 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "lint:fix": "next lint --fix" }, "dependencies": { - "@aleph-front/aleph-core": "^1.3.10", + "@aleph-front/aleph-core": "^1.3.13", "@fortawesome/fontawesome-svg-core": "^6.3.0", "@hookform/resolvers": "^3.1.1", "@types/node": "18.14.1", diff --git a/src/components/common/HiddenFileInput/cmp.tsx b/src/components/common/HiddenFileInput/cmp.tsx index 2cac394b..7e049b97 100644 --- a/src/components/common/HiddenFileInput/cmp.tsx +++ b/src/components/common/HiddenFileInput/cmp.tsx @@ -1,73 +1,85 @@ -import React, { ChangeEvent, memo, useCallback, useRef } from 'react' +import React, { + ChangeEvent, + ForwardedRef, + forwardRef, + memo, + useCallback, + useRef, +} from 'react' import { Button, FormError, Icon } from '@aleph-front/aleph-core' import { HiddenFileInputProps } from './types' import { StyledHiddenFileInput } from './styles' import { ellipseAddress } from '@/helpers/utils' export const HiddenFileInput = memo( - ({ onChange, accept, value, children, error }: HiddenFileInputProps) => { - const inputRef = useRef(null) + forwardRef( + ( + { onChange, accept, value, children, error }: HiddenFileInputProps, + ref: ForwardedRef, + ) => { + const inputRef = useRef(null) - const handleClick = useCallback(() => { - if (!inputRef.current) return - inputRef.current.click() - }, []) + const handleClick = useCallback(() => { + if (!inputRef.current) return + inputRef.current.click() + }, []) - const handleRemoveFile = useCallback(() => { - onChange(undefined) - }, [onChange]) + const handleRemoveFile = useCallback(() => { + onChange(undefined) + }, [onChange]) - const handleChange = useCallback( - (e: ChangeEvent) => { - // This is verbose to avoid a type error on e.target.files[0] being undefined - const target = e.target as HTMLInputElement - const { files } = target + const handleChange = useCallback( + (e: ChangeEvent) => { + // This is verbose to avoid a type error on e.target.files[0] being undefined + const target = e.target as HTMLInputElement + const { files } = target - if (files) { - const fileUploaded = files[0] - onChange(fileUploaded) - } - }, - [onChange], - ) + if (files) { + const fileUploaded = files[0] + onChange(fileUploaded) + } + }, + [onChange], + ) - return ( - <> - {value ? ( - - ) : ( - - )} + return ( +
+ {value ? ( + + ) : ( + + )} - {error && } + {error && } - - - ) - }, + +
+ ) + }, + ), ) HiddenFileInput.displayName = 'HiddenFileInput' diff --git a/src/components/form/AddDomains/types.ts b/src/components/form/AddDomains/types.ts index 6f4a0472..f5b4a696 100644 --- a/src/components/form/AddDomains/types.ts +++ b/src/components/form/AddDomains/types.ts @@ -1,3 +1,4 @@ +import { EntityType } from '@/helpers/constants' import { Control } from 'react-hook-form' export type DomainItemProps = { @@ -10,4 +11,5 @@ export type DomainItemProps = { export type AddDomainsProps = { control: Control name: string + entityType: EntityType.Program | EntityType.Instance } diff --git a/src/components/form/AddSSHKeys/cmp.tsx b/src/components/form/AddSSHKeys/cmp.tsx index 380cf8e5..a0b8124a 100644 --- a/src/components/form/AddSSHKeys/cmp.tsx +++ b/src/components/form/AddSSHKeys/cmp.tsx @@ -1,5 +1,11 @@ import React from 'react' -import { Icon, TextInput, Button, Checkbox } from '@aleph-front/aleph-core' +import { + Icon, + TextInput, + Button, + Checkbox, + FormError, +} from '@aleph-front/aleph-core' import { useAddSSHKeys, useSSHKeyItem } from '@/hooks/form/useAddSSHKeys' import { SSHKeyItemProps, AddSSHKeysProps } from './types' import NoisyContainer from '@/components/common/NoisyContainer' @@ -16,47 +22,56 @@ const SSHKeyItem = React.memo((props: SSHKeyItemProps) => { } = useSSHKeyItem(props) return ( -
-
- -
-
-
- -
-
- + {isSelectedCtrl.fieldState.error && ( + + )} +
+
+
- {allowRemove && ( -
- {isNew && ( - - )} +
+
+ +
+
+
- )} + {allowRemove && ( +
+ {isNew && ( + + )} +
+ )} +
-
+ ) }) SSHKeyItem.displayName = 'SSHKeyItem' diff --git a/src/components/form/AddVolume/cmp.tsx b/src/components/form/AddVolume/cmp.tsx index 8dd29aff..e011f88c 100644 --- a/src/components/form/AddVolume/cmp.tsx +++ b/src/components/form/AddVolume/cmp.tsx @@ -133,6 +133,7 @@ const AddExistingVolume = React.memo((props: AddExistingVolumeProps) => {
@@ -144,8 +145,14 @@ const AddExistingVolume = React.memo((props: AddExistingVolumeProps) => { AddExistingVolume.displayName = 'AddExistingVolume' const AddPersistentVolume = React.memo((props: AddPersistentVolumeProps) => { - const { nameCtrl, mountPathCtrl, sizeCtrl, volumeSize, handleRemove } = - useAddPersistentVolumeProps(props) + const { + nameCtrl, + mountPathCtrl, + sizeCtrl, + sizeValue, + sizeHandleChange, + handleRemove, + } = useAddPersistentVolumeProps(props) return ( <> @@ -176,9 +183,11 @@ const AddPersistentVolume = React.memo((props: AddPersistentVolumeProps) => {
{handleRemove && } diff --git a/src/components/form/SelectInstanceImage/cmp.tsx b/src/components/form/SelectInstanceImage/cmp.tsx index a741ea1e..1eb22b93 100644 --- a/src/components/form/SelectInstanceImage/cmp.tsx +++ b/src/components/form/SelectInstanceImage/cmp.tsx @@ -1,63 +1,81 @@ /* eslint-disable @next/next/no-img-element */ +import React, { KeyboardEvent } from 'react' import { useBasePath } from '@/hooks/common/useBasePath' -import { useCallback } from 'react' +import { ForwardedRef, forwardRef, memo, useCallback } from 'react' import { useSelectInstanceImage } from '@/hooks/form/useSelectInstanceImage' import { SelectInstanceImageItemProps, SelectInstanceImageProps } from './types' import { StyledFlatCard, StyledFlatCardContainer } from './styles' -import React from 'react' import { FormError } from '@aleph-front/aleph-core' -const SelectInstanceImageItem = React.memo( - ({ option, value, onChange }: SelectInstanceImageItemProps) => { - const selected = value === option.id - const basePath = useBasePath() - const imgPrefix = `${basePath}/img` +const SelectInstanceImageItem = memo( + forwardRef( + ( + { option, index, value, onChange }: SelectInstanceImageItemProps, + ref: ForwardedRef, + ) => { + const selected = value === option.id + const basePath = useBasePath() + const imgPrefix = `${basePath}/img` - const handleClick = useCallback(() => { - if (option.disabled) return - onChange(option.id) - }, [option, onChange]) + const handleClick = useCallback(() => { + if (option.disabled) return + onChange(option.id) + }, [option, onChange]) - return ( - - {`${option.name} - {option.name} - - ) - }, + const handleKeyDown = useCallback( + (e: KeyboardEvent) => { + if (e.code !== 'Space' && e.code !== 'Enter') return + e.preventDefault() + onChange(option.id) + }, + [option, onChange], + ) + + return ( + + {`${option.name} + {option.name} + + ) + }, + ), ) SelectInstanceImageItem.displayName = 'SelectInstanceImageItem' -export const SelectInstanceImage = React.memo( - (props: SelectInstanceImageProps) => { - const { imageCtrl, options } = useSelectInstanceImage(props) +export const SelectInstanceImage = memo((props: SelectInstanceImageProps) => { + const { imageCtrl, options } = useSelectInstanceImage(props) - return ( -
- - {options.map((option) => ( - - ))} - - {imageCtrl.fieldState.error && ( - - )} -
- ) - }, -) + return ( +
+ + {options.map((option, index) => ( + + ))} + + {imageCtrl.fieldState.error && ( + + )} +
+ ) +}) SelectInstanceImage.displayName = 'SelectInstanceImage' export default SelectInstanceImage diff --git a/src/components/form/SelectInstanceImage/types.ts b/src/components/form/SelectInstanceImage/types.ts index adfecf13..5d7ee4d7 100644 --- a/src/components/form/SelectInstanceImage/types.ts +++ b/src/components/form/SelectInstanceImage/types.ts @@ -4,6 +4,7 @@ import { Control } from 'react-hook-form' export type SelectInstanceImageItemProps = { option: InstanceImage + index: number value?: InstanceImageField onChange: (value: InstanceImageField) => void } diff --git a/src/components/form/SelectInstanceSpecs/cmp.tsx b/src/components/form/SelectInstanceSpecs/cmp.tsx index f0a6002e..988ae019 100644 --- a/src/components/form/SelectInstanceSpecs/cmp.tsx +++ b/src/components/form/SelectInstanceSpecs/cmp.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { KeyboardEvent, memo } from 'react' /* eslint-disable @next/next/no-img-element */ import { useSelectInstanceSpecs } from '@/hooks/form/useSelectInstanceSpecs' import { Button, FormError, Icon, TableColumn } from '@aleph-front/aleph-core' @@ -9,136 +9,142 @@ import { StyledTable } from './styles' import { Executable } from '@/domain/executable' import { EntityType } from '@/helpers/constants' -export const SelectInstanceSpecs = React.memo( - (props: SelectInstanceSpecsProps) => { - const { specsCtrl, options, type, isPersistent } = - useSelectInstanceSpecs(props) +export const SelectInstanceSpecs = memo((props: SelectInstanceSpecsProps) => { + const { specsCtrl, options, type, isPersistent } = + useSelectInstanceSpecs(props) - const columns = useMemo(() => { - const cols = [ - { - label: 'Cores', - sortable: true, - sortBy: (row: SpecsDetail) => row.specs.cpu, - render: (row: SpecsDetail) => ( - {row.specs.cpu} x86 64bit - ), + const columns = useMemo(() => { + const cols = [ + { + label: 'Cores', + sortable: true, + sortBy: (row: SpecsDetail) => row.specs.cpu, + render: (row: SpecsDetail) => ( + {row.specs.cpu} x86 64bit + ), + }, + { + label: 'Memory', + align: 'right', + sortable: true, + sortBy: (row: SpecsDetail) => row.ram, + render: (row: SpecsDetail) => ( + {row.ram} + ), + }, + { + label: 'Hold', + align: 'right', + sortable: true, + sortBy: (row: SpecsDetail) => row.price, + render: (row: SpecsDetail) => ( + {row.price} + ), + }, + { + label: '', + align: 'right', + render: (row: SpecsDetail) => { + return ( + + ) }, - { - label: 'Memory', - align: 'right', - sortable: true, - sortBy: (row: SpecsDetail) => row.ram, - render: (row: SpecsDetail) => ( - {row.ram} - ), - }, - { - label: 'Hold', - align: 'right', - sortable: true, - sortBy: (row: SpecsDetail) => row.price, - render: (row: SpecsDetail) => ( - {row.price} - ), - }, - { - label: '', - align: 'right', - render: (row: SpecsDetail) => { - return ( - - ) - }, - }, - ] as TableColumn[] + }, + ] as TableColumn[] - if (type === EntityType.Instance) { - cols.splice(2, 0, { - label: 'Storage', - align: 'right', - sortable: true, - sortBy: (row: SpecsDetail) => row.storage, - render: (row: SpecsDetail) => { - return {row.storage} - }, - }) - } + if (type === EntityType.Instance) { + cols.splice(2, 0, { + label: 'Storage', + align: 'right', + sortable: true, + sortBy: (row: SpecsDetail) => row.storage, + render: (row: SpecsDetail) => { + return {row.storage} + }, + }) + } - return cols - }, [type]) + return cols + }, [type]) - const data: SpecsDetail[] = useMemo(() => { - return options.map((specs) => { - const { ram, storage } = specs - const price = Executable.getExecutableCost({ - type, - specs, - isPersistent, - }) + const data: SpecsDetail[] = useMemo(() => { + return options.map((specs) => { + const { ram, storage } = specs + const price = Executable.getExecutableCost({ + type, + specs, + isPersistent, + }) - const isActive = specsCtrl.field.value.cpu === specs.cpu - const className = `${isActive ? 'text-main0' : ''}` + const isActive = specsCtrl.field.value.cpu === specs.cpu + const className = `${isActive ? 'text-main0' : ''}` - return { - specs, - isActive, - className, - storage: convertBitUnits(storage, { - from: 'mb', - to: 'gb', - displayUnit: true, - }) as string, - ram: convertBitUnits(ram, { - from: 'mb', - to: 'gb', - displayUnit: true, - }) as string, - price: price.computeTotalCost + ' ALEPH', - } - }) - }, [options, type, isPersistent, specsCtrl.field.value]) + return { + specs, + isActive, + className, + storage: convertBitUnits(storage, { + from: 'mb', + to: 'gb', + displayUnit: true, + }) as string, + ram: convertBitUnits(ram, { + from: 'mb', + to: 'gb', + displayUnit: true, + }) as string, + price: price.computeTotalCost + ' ALEPH', + } + }) + }, [options, type, isPersistent, specsCtrl.field.value]) - const getRowKey = useCallback((row: SpecsDetail) => row.specs.cpu + '', []) + const getRowKey = useCallback((row: SpecsDetail) => row.specs.cpu + '', []) - const { onChange } = specsCtrl.field + const { onChange, ref } = specsCtrl.field - const handleRowProps = useCallback( - (row: SpecsDetail) => ({ - onClick: () => onChange(row.specs), - }), - [onChange], - ) + const handleRowProps = useCallback( + (row: SpecsDetail, rowIndex: number) => ({ + onClick: () => onChange(row.specs), + onKeyDown: (e: KeyboardEvent) => { + if (e.code !== 'Space' && e.code !== 'Enter') return + e.preventDefault() + onChange(row.specs) + }, + tabIndex: 0, + ref: rowIndex === 0 ? ref : undefined, + }), + [onChange, ref], + ) - return ( -
- - {specsCtrl.fieldState.error && ( - - )} -
- ) - }, -) + return ( +
+ + {specsCtrl.fieldState.error && ( + + )} +
+ ) +}) SelectInstanceSpecs.displayName = 'SelectInstanceSpecs' export default SelectInstanceSpecs diff --git a/src/components/pages/dashboard/NewFunctionPage/cmp.tsx b/src/components/pages/dashboard/NewFunctionPage/cmp.tsx index 17601329..b55b0daf 100644 --- a/src/components/pages/dashboard/NewFunctionPage/cmp.tsx +++ b/src/components/pages/dashboard/NewFunctionPage/cmp.tsx @@ -1,4 +1,4 @@ -import { Button, Tabs, TextGradient } from '@aleph-front/aleph-core' +import { Button, Tabs } from '@aleph-front/aleph-core' import { EntityType } from '@/helpers/constants' import CompositeTitle from '@/components/common/CompositeTitle' import { useNewFunctionPage } from '@/hooks/pages/dashboard/useNewFunctionPage' @@ -14,211 +14,202 @@ import AddFunctionCode from '@/components/form/AddFunctionCode' import SelectFunctionPersistence from '@/components/form/SelectFunctionPersistence' import BorderBox from '@/components/common/BorderBox' import { convertBitUnits } from '@/helpers/utils' +import Form from '@/components/form/Form' export default function NewFunctionPage() { const { address, accountBalance, isCreateButtonDisabled, - handleSubmit, - handleChangeEntityTab, values, control, + errors, + handleSubmit, + handleChangeEntityTab, } = useNewFunctionPage() return ( - <> -
-
- - - -
-
- - - Code to execute - -

- If your code has any dependencies, you can upload them separately - in the volume section below to ensure a faster creation. -

- -
-
-
- - - Select runtime - -

- Select the optimal environment for executing your functions, - tailored to your specific requirements. Below are the available - options -

- -
-
-
- - - Type of scheduling - -

- Configure if this program should be running continuously, - persistent, or only on-demand in response to a user request or an - event. -

- -
-
-
- - - Select an instance size - -

- Select the hardware resources allocated to your functions, - ensuring optimal performance and efficient resource usage tailored - to your specific needs. -

- -
-
-
- - - Name and tags - -

- Organize and identify your functions more effectively by assigning - a unique name, obtaining a hash reference, and defining multiple - tags. This helps streamline your development process and makes it - easier to manage your web3 functions. -

- -
-
-
- - - Add volumes - - {values.specs && ( - - Good news! Your selected package already includes{' '} - - {convertBitUnits(values.specs.storage, { - from: 'mb', - to: 'gb', - displayUnit: true, - })} - {' '} - of storage at no additional cost. This has been factored into - your configuration to maximize efficiency and value. Feel free - to adjust as necessary. - - )} - - -
-
- - - Add environment variables - -

- Define key-value pairs that act as configuration settings for your - web3 function. Environment variables offer a convenient way to - store information, manage configurations, and modify your - application's behaviour without altering the source code. -

- -
-
-
- - - Custom domain - -

- Configure a user-friendly domain name for your web3 function, - providing a more accessible and professional way for users to - interact with your application. -

- -
-
-
- - - Estimated holding requirements - -
-

- This amount needs to be present in your wallet until the - function is removed. Tokens won’t be locked nor consumed. The - function will be garbage collected once funds are removed from - the wallet. -

-
-
- -
-
- -
-
-
-
- +
+
+ + + +
+
+ + + Code to execute + +

+ If your code has any dependencies, you can upload them separately in + the volume section below to ensure a faster creation. +

+ +
+
+
+ + + Select runtime + +

+ Select the optimal environment for executing your functions, + tailored to your specific requirements. Below are the available + options +

+ +
+
+
+ + + Type of scheduling + +

+ Configure if this program should be running continuously, + persistent, or only on-demand in response to a user request or an + event. +

+ +
+
+
+ + + Select an instance size + +

+ Select the hardware resources allocated to your functions, ensuring + optimal performance and efficient resource usage tailored to your + specific needs. +

+ +
+
+
+ + + Name and tags + +

+ Organize and identify your functions more effectively by assigning a + unique name, obtaining a hash reference, and defining multiple tags. + This helps streamline your development process and makes it easier + to manage your web3 functions. +

+ +
+
+
+ + + Add volumes + + {values.specs && ( + + Good news! Your selected package already includes{' '} + + {convertBitUnits(values.specs.storage, { + from: 'mb', + to: 'gb', + displayUnit: true, + })} + {' '} + of storage at no additional cost. This has been factored into your + configuration to maximize efficiency and value. Feel free to + adjust as necessary. + + )} + + +
+
+ + + Add environment variables + +

+ Define key-value pairs that act as configuration settings for your + web3 function. Environment variables offer a convenient way to store + information, manage configurations, and modify your + application's behaviour without altering the source code. +

+ +
+
+
+ + + Custom domain + +

+ Configure a user-friendly domain name for your web3 function, + providing a more accessible and professional way for users to + interact with your application. +

+ +
+
+ + This amount needs to be present in your wallet until the function is + removed. Tokens won't be locked nor consumed. The function will + be garbage collected once funds are removed from the wallet. + + } + button={ + + } + /> + ) } diff --git a/src/components/pages/dashboard/NewInstancePage/cmp.tsx b/src/components/pages/dashboard/NewInstancePage/cmp.tsx index 79a23d48..ceed25dd 100644 --- a/src/components/pages/dashboard/NewInstancePage/cmp.tsx +++ b/src/components/pages/dashboard/NewInstancePage/cmp.tsx @@ -1,4 +1,4 @@ -import { Button, Tabs, TextGradient } from '@aleph-front/aleph-core' +import { Button, Tabs } from '@aleph-front/aleph-core' import CompositeTitle from '@/components/common/CompositeTitle' import SelectInstanceImage from '@/components/form/SelectInstanceImage' import SelectInstanceSpecs from '@/components/form/SelectInstanceSpecs' @@ -11,198 +11,186 @@ import HoldingRequirements from '@/components/common/HoldingRequirements' import { EntityType } from '@/helpers/constants' import Container from '@/components/common/CenteredContainer' import { useNewInstancePage } from '@/hooks/pages/dashboard/useNewInstancePage' +import Form from '@/components/form/Form' export default function NewInstancePage() { const { address, accountBalance, isCreateButtonDisabled, - handleSubmit, - handleChangeEntityTab, values, control, + errors, + handleSubmit, + handleChangeEntityTab, } = useNewInstancePage() return ( - <> -
-
- - - -
-
- - - Choose an image - -

- Chose a base image for your VM. It’s the base system that you will - be able to customize. -

-
- -
-
-
-
- - - Select an instance size - -

- Please select one of the available instance size as a base for - your VM. You will be able to customize the volumes later. -

-
- -
-
-
-
- - - Add volumes - -
- -
-
-
-
- - - Configure SSH Key - -

- Access your cloud instances securely. Give existing key’s below - access to this instance or add new keys. Remember, storing private - keys safely is crucial for security. If you need help, our support - team is always ready to assist. -

-
- -
-
-
-
- - - Add environment variables - -

- Define key-value pairs that act as configuration settings for your - web3 instance. Environment variables offer a convenient way to - store information, manage configurations, and modify your - application's behaviour without altering the source code. -

-
- -
-
-
-
- - - Custom domain - -

- You have the ability to configure a domain name to access your - cloud instances. By setting up a user-friendly custom domain, - accessing your instances becomes easier and more intuitive. - It&s another way we&re making web3 cloud management as - straightforward as possible. -

- -
-
-
- - - Name and tags - -

- Organize and identify your instances more effectively by assigning - a unique name, obtaining a hash reference, and defining multiple - tags. This helps streamline your development process and makes it - easier to manage your web3 instances. -

- +
+ + + +
+
+ + + Choose an image + +

+ Chose a base image for your VM. It’s the base system that you will + be able to customize. +

+
+ +
+
+
+
+ + + Select an instance size + +

+ Please select one of the available instance size as a base for your + VM. You will be able to customize the volumes later. +

+
+ - -
-
- - - Estimated holding requirements - -
-

- This amount needs to be present in your wallet until the - instance is removed. Tokens won’t be locked nor consumed. The - instance will be garbage collected once funds are removed from - the wallet. -

-
-
- -
-
- -
-
-
- - +
+ + +
+ + + Add volumes + +
+ +
+
+
+
+ + + Configure SSH Key + +

+ Access your cloud instances securely. Give existing key’s below + access to this instance or add new keys. Remember, storing private + keys safely is crucial for security. If you need help, our support + team is always ready to assist. +

+
+ +
+
+
+
+ + + Add environment variables + +

+ Define key-value pairs that act as configuration settings for your + web3 instance. Environment variables offer a convenient way to store + information, manage configurations, and modify your + application's behaviour without altering the source code. +

+
+ +
+
+
+
+ + + Custom domain + +

+ You have the ability to configure a domain name to access your cloud + instances. By setting up a user-friendly custom domain, accessing + your instances becomes easier and more intuitive. It&s another + way we&re making web3 cloud management as straightforward as + possible. +

+ +
+
+
+ + + Name and tags + +

+ Organize and identify your instances more effectively by assigning a + unique name, obtaining a hash reference, and defining multiple tags. + This helps streamline your development process and makes it easier + to manage your web3 instances. +

+ +
+
+ + This amount needs to be present in your wallet until the instance is + removed. Tokens won't be locked nor consumed. The instance will + be garbage collected once funds are removed from the wallet. + + } + button={ + + } + /> + ) } diff --git a/src/components/pages/dashboard/NewSSHKeyPage/cmp.tsx b/src/components/pages/dashboard/NewSSHKeyPage/cmp.tsx index 72a0e5c0..99e0f27a 100644 --- a/src/components/pages/dashboard/NewSSHKeyPage/cmp.tsx +++ b/src/components/pages/dashboard/NewSSHKeyPage/cmp.tsx @@ -9,52 +9,50 @@ export default function NewSSHKey() { const { keyCtrl, labelCtrl, handleSubmit, errors } = useNewSSHKeyPage() return ( - <> -
-
- - - Configure SSH Key - -

- Access your cloud instances securely. Give existing key’s below - access to this instance or add new keys. Remember, storing private - keys safely is crucial for security. If you need help, our support - team is always ready to assist. -

- -
-