diff --git a/web/src/App.tsx b/web/src/App.tsx index 8be7bf1..8e03e8b 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -11,6 +11,7 @@ import { loadActiveCertificate } from './recoil/api'; import { useWallet } from './hooks/useWallet'; // Lazy loading all pages in appropriate time +const NewLanding = lazy(() => import('./components/NewLanding')); const DeploymentStepper = lazy(() => import('./components/DeploymentStepper')); const Deployment = lazy(() => import('./components/Deployment')); const ReDeploy = lazy(() => import('./pages/ReDeploy')); @@ -24,7 +25,7 @@ const Welcome = () => { const navigate = useNavigate(); useEffect(() => { - navigate('/landing/node-deployment'); + navigate('/landing'); }, []); return <>; @@ -43,7 +44,10 @@ const AppRouter = () => { } /> - }/> + + } /> + }/> + } /> } /> diff --git a/web/src/components/Icons/index.tsx b/web/src/components/Icons/index.tsx index 54f15fe..c8649a8 100644 --- a/web/src/components/Icons/index.tsx +++ b/web/src/components/Icons/index.tsx @@ -613,6 +613,25 @@ export const IconAdd: React.FC> = () => { ); }; +export const IconWWW: React.FC> = () => { + return + + ; +}; + +export const IconXRayView: React.FC> = () => { + return ; +}; +export const IconElectronicsChip: React.FC> = () => { + return ; +}; +export const IconCode: React.FC> = () => { + return ; +}; + const iconMap = { redeploy: IconRedeploy, edit: IconEdit, @@ -643,6 +662,10 @@ const iconMap = { share: IconShare, newDeploy: IconNewDeployment, logoAkashConsole: IconLogoAkashConsole, + www: IconWWW, + xrayView: IconXRayView, + electronicsChip: IconElectronicsChip, + code: IconCode, }; export type IconType = keyof typeof iconMap; diff --git a/web/src/components/NewLanding/CategoryCardsContainer.tsx b/web/src/components/NewLanding/CategoryCardsContainer.tsx new file mode 100644 index 0000000..93384b5 --- /dev/null +++ b/web/src/components/NewLanding/CategoryCardsContainer.tsx @@ -0,0 +1,197 @@ +import React, { useCallback, useState } from 'react'; +import styled from '@emotion/styled'; +import { css } from '@emotion/react'; +import { Icon, IconType } from '../Icons'; +import { useNavigate } from 'react-router-dom'; +import { SdlEditor } from '../SdlConfiguration/SdllEditor'; +import { Button } from '@mui/material'; + +export const CategoryCardsContainer: React.FC<{ + categoriesTiles: { + tiles: { + title: string, + description: string, + route: string, + icon: string, + buttonEnabled: boolean, + buttonText: string, + }[], + }, + setFieldValue: any, +}> = ({categoriesTiles, setFieldValue }) => { + const navigate = useNavigate(); + const [reviewSdl, showSdlReview] = useState(false); + const closeReviewModal = useCallback(() => showSdlReview(false), []); + + + const customSDLTile = { + title: 'Custom Application', + description: 'Define your unique deployment requirements and preferences with SDL and deploy with ease on the flexible and reliable Akash network.', + buttonText: 'Import SDL', + icon: 'code', + buttonEnabled: true + }; + + return + {categoriesTiles?.tiles?.map((c) => { + return + + + + + + {c.title} + + + + {c.description} + + { + navigate(c.route); + }} + disabled={!c.buttonEnabled} + > + {c.buttonEnabled ? c.buttonText : 'Coming soon...'} + + ; + })} + + + + + + + + {customSDLTile.title} + + + + {customSDLTile.description} + + { + showSdlReview(true); + setFieldValue('sdl', {}); + }} + disabled={!customSDLTile.buttonEnabled} + > + {customSDLTile.buttonEnabled ? customSDLTile.buttonText : 'Coming soon...'} + + + + navigate('/new-deployment/custom-sdl', { state: { sdl: sdl } }) + } + /> + ; +}; + +const CategoryCardsWrapper = styled.div` + display: flex; + flex-wrap: wrap; + justify-content: flex-start; + gap: 16px; +`; + +const CategoryCard = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: space-between; + padding: 24px; + gap: 24px; + + background: #FFFFFF; + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: 8px; + + flex: 1; + min-width: 330.75px; + min-height: 288px; +`; + +const CategoryCardDescription = styled.div` + font-style: normal; + font-weight: 500; + font-size: 16px; + line-height: 24px; + color: rgba(0, 0, 0, 0.5); +`; + +const CategoryCardHeading = styled.h2` + font-weight: 700; + font-size: 18px; + line-height: 22px; + color: #111827; +`; + +const CategoryCardHeaderWithIcon = styled.div` + display: flex; + flex-direction: row; + align-items: center; + padding: 0px; + gap: 16px; +`; + +const IconWrapper = styled.div` + width: 56px; + height: 56px; + background: #EDEDED; + border-radius: 40px; + display: flex; + align-items: center; + justify-content: center; +`; + +const GeneralButtonStyle = css` + font-family: 'Satoshi-medium', serif; + font-style: normal; + font-weight: 500; + font-size: 16px; + padding: 13px 25px 13px 25px; + line-height: 15px; + color: #1C1B1B; + width: auto; + margin-top: 20px; + gap: 8px; + text-transform: capitalize; + background-color: #FFFFFF; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + border: 1px solid #D7D7D7; + border-radius: 6px; + + &:hover { + background-color: #F9FAFB; + border: 1px solid #D1D5DB; + } +`; + +const ChooseTemplateButton = styled(Button)` + ${GeneralButtonStyle} + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + padding: 8px 16px; + gap: 10px; + + background: #FFFFFF; + border: 1px solid #D8D7D7; + border-radius: 6px; + + font-style: normal; + font-weight: 500; + font-size: 14px; + line-height: 24px; + color: #0F172A; + + /* overriding GeneralButtonStyle */ + width: 100%; +`; diff --git a/web/src/components/NewLanding/SdlGuideContainer.tsx b/web/src/components/NewLanding/SdlGuideContainer.tsx new file mode 100644 index 0000000..6cd514a --- /dev/null +++ b/web/src/components/NewLanding/SdlGuideContainer.tsx @@ -0,0 +1,102 @@ +import React from 'react'; +import styled from '@emotion/styled'; + + +export const SdlGuideContainer: React.FC<{ + sdlGuideTiles: { + introText: string; + introDescription: string; + tiles: { + step: string; + text: string; + image: string }[] + } +}> = ({ sdlGuideTiles }) => { + return + + + {sdlGuideTiles?.introText} + + + {sdlGuideTiles?.introDescription} + + + + {sdlGuideTiles?.tiles?.map((c) => { + return ( + + + + + {c.step} + {c.text} + + ); + })} + ; +}; + +const SdlGuideCardsWrapper = styled.div` + display: flex; + flex-wrap: wrap; + justify-content: flex-start; + + padding: 24px; + gap: 24px; + + width: 100%; + + background: #FFFFFF; + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: 8px; +`; + + +const GuideImageWrapper = styled.div` + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: 6px; + display: flex; + justify-content: center; + align-items: center; +`; + + +const GuideIntroDescription = styled.div` + font-size: 16px; + line-height: 24px; + margin-top: 8px; + + color: rgba(0, 0, 0, 0.5); +`; + +const GuideHeader = styled.h3` + font-weight: 700; + font-size: 18px; + line-height: 20px; + + + color: #111827; +`; + +const GuideIntroHeader = styled.h2` + font-weight: 700; + font-size: 24px; + line-height: 32px; + + color: #111827; +`; + +const GuideCard = styled.div` + display: flex; + flex-direction: column; + justify-content: space-around; + + flex: 1; + min-width: 318.75px; + height: 280px; +`; + +const GuideIntroCard = styled(GuideCard)` + justify-content: flex-start; + height: auto; +`; \ No newline at end of file diff --git a/web/src/components/NewLanding/index.tsx b/web/src/components/NewLanding/index.tsx new file mode 100644 index 0000000..8152ea6 --- /dev/null +++ b/web/src/components/NewLanding/index.tsx @@ -0,0 +1,159 @@ +import React, {useState } from 'react'; +import styled from '@emotion/styled'; +import { useNavigate, useParams } from 'react-router-dom'; +import { + Box, + Divider, +} from '@mui/material'; +import { useQuery } from 'react-query'; +import { fetchLandingPageMetadata } from '../../recoil/api/sdl'; +import { useRecoilState, useRecoilValue } from 'recoil'; +import { Formik } from 'formik'; +import { deploymentSdl, keplrState, myDeployments as myDeploymentsAtom } from '../../recoil/atoms'; +import { createDeployment } from '../../recoil/api'; +import { Dialog } from '../Dialog'; +import { initialValues, InitialValuesProps } from '../SdlConfiguration/settings'; +import { myDeploymentFormat } from '../../_helpers/my-deployment-utils'; +import { Deployment } from '@akashnetwork/akashjs/build/protobuf/akash/deployment/v1beta2/deployment'; +import { SdlGuideContainer } from './SdlGuideContainer'; +import { CategoryCardsContainer } from './CategoryCardsContainer'; + +export interface DeploymentStepperProps { + dseq?: string; + leaseId?: string; +} + + +const DeploymentStepper: React.FC = () => { + const keplr = useRecoilValue(keplrState); + const navigate = useNavigate(); + const [deploymentId, setDeploymentId] = React.useState<{ owner: string; dseq: string }>(); + const { folderName, templateId, intentId, dseq } = useParams(); + const [sdl, setSdl] = useRecoilState(deploymentSdl); + const [progressVisible, setProgressVisible] = useState(false); + const [cardMessage, setCardMessage] = useState(''); + const [activeStep, setActiveStep] = useState({ currentCard: 0 }); + const [open, setOpen] = React.useState(false); + const [errorTitle, setErrorTitle] = React.useState(); + const [errorMessage, setErrorMessage] = React.useState(); + const [myDeployments, setMyDeployments] = useRecoilState(myDeploymentsAtom); + + + const { data: landingPageMetadata } = useQuery('landingPageMetadata', fetchLandingPageMetadata, { + refetchOnWindowFocus: false, + keepPreviousData: true, + }); + + React.useEffect(() => { + if (dseq) { + setDeploymentId({ + owner: keplr.accounts[0].address, + dseq, + }); + setActiveStep({ currentCard: 4 }); + return; + } + }, [dseq, keplr]); + + + const handleDeployment = (key: string, deployment: any) => { + const newDeployments: { [key: string]: Deployment } = { ...myDeployments }; + newDeployments[key] = deployment; + setMyDeployments(newDeployments); + }; + + // Error handling dialog + const handleClose = (reason: string) => { + if (reason === 'closeButtonClick') { + setOpen(false); + } + }; + + // TODO: this should be changed to use the logging system, and not throw + // additional exceptions. + const handleError = async (maybeError: unknown, method: string) => { + const error = maybeError && Object.prototype.hasOwnProperty.call(maybeError, 'message') + ? (maybeError as Error) + : { message: 'Unknown error' }; + + let title = 'Error'; + let message = 'An error occurred while sending your request.'; + if (method === 'acceptBid') { + title = 'Error Select Provider'; + message = 'An error occurred while selecting a provider.'; + } + if (method === 'createDeployment') { + title = 'Error Create Deployment'; + message = 'An error occurred while trying to deploy.'; + if (error.message.includes('Query failed with (6)')) { + message = 'There was an RPC error. This may happen during upgrades to the Akash Network.'; + } + } + setErrorTitle(title); + setErrorMessage(message); + setProgressVisible(false); + setCardMessage(''); + setOpen(true); + throw new Error(`${method}: ${error.message}`); + }; + + return ( + + { + setProgressVisible(true); + console.log('sometime soon'); + setCardMessage('Creating deployment'); + + try { + const result = await createDeployment(keplr, value.sdl, value.depositor); + if (result && result.deploymentId) { + setDeploymentId(result.deploymentId); + setSdl(value.sdl); + + // wait a second to give the blockchain a chance to setup + // the bids for the deployment + setTimeout(() => { + setProgressVisible(false); + navigate(`/configure-deployment/${result.deploymentId.dseq}`); + }, 2000); + + // set deployment to localStorage object using Atom + const _deployment = await myDeploymentFormat(result, value); + handleDeployment(_deployment.key, JSON.stringify(_deployment.data)); + + // set deployment to localStorage item by dseq (deprecate ?) + localStorage.setItem(_deployment.key, JSON.stringify(_deployment.data)); + } + } catch (error) { + await handleError(error, 'createDeployment'); + } + }} + > + {({ setFieldValue }) => { + return ( + <> + What would you like to do today? + + + + + + ); + }} + + + ); +}; + +const IntroHeading = styled.h2<{ fontWeight?: number }>` + font-weight: ${(props) => props?.fontWeight ?? 700}; + font-size: 24px; + line-height: 32px; + color: #111827; +`; + +export default DeploymentStepper; + diff --git a/web/src/components/SideNav/index.tsx b/web/src/components/SideNav/index.tsx index 32696f9..7665cd2 100644 --- a/web/src/components/SideNav/index.tsx +++ b/web/src/components/SideNav/index.tsx @@ -162,6 +162,19 @@ export default function SideNav(props: any) { My Deployments + {/* PRODUCT-CLARIFY: where should providers navlink be linked to, we only have the /providers/:providerId route */} + (isActive ? 'selected-active' : 'selected-inactive')} + > + + + + + Providers + +
diff --git a/web/src/recoil/api/sdl.ts b/web/src/recoil/api/sdl.ts index 211c675..65fe76f 100644 --- a/web/src/recoil/api/sdl.ts +++ b/web/src/recoil/api/sdl.ts @@ -42,6 +42,23 @@ export const fetchTemplateList: QueryFunction = async ({queryKey}) return JSON.parse(data); }; +export const fetchLandingPageMetadata: QueryFunction = async ({queryKey}) => { + const octokit = new Octokit({}); + + const repository = await octokit.request('GET /repos/{owner}/{repo}/contents/{path}/metadata.json', { + owner: 'akash-network', + repo: 'deploy-templates', + path: 'landing', + }); + + const data = Buffer.from( + repository.data.content, + repository.data.encoding + ).toString('utf-8'); + + return JSON.parse(data); +}; + // export const templateList = [ // { diff --git a/web/src/style/app.css b/web/src/style/app.css index 1859ab7..69db40f 100644 --- a/web/src/style/app.css +++ b/web/src/style/app.css @@ -1266,6 +1266,7 @@ a { #link_new_deployment, #link_my_deployments, +#link_providers, #link_settings, #link_help { width: 100%; @@ -1276,6 +1277,7 @@ a { #link_new_deployment:hover, #link_my_deployments:hover, +#link_providers:hover, #link_settings:hover, #link_help:hover { border: 1px solid #f0f1f4; @@ -1293,6 +1295,12 @@ a { fill: #6b7280; } +#link_providers:hover * { + color: #111827; + stroke: #6b7280; +} + + #link_my_deployments:hover * { color: #111827; stroke: #6b7280; diff --git a/web/src/style/app.scss b/web/src/style/app.scss index 24acfaf..7ed997a 100644 --- a/web/src/style/app.scss +++ b/web/src/style/app.scss @@ -246,6 +246,7 @@ a { #link_new_deployment, #link_my_deployments, +#link_providers, #link_settings, #link_help { width: 100%; @@ -256,6 +257,7 @@ a { #link_new_deployment:hover, #link_my_deployments:hover, +#link_providers:hover, #link_settings:hover, #link_help:hover { border: 1px solid #f0f1f4; @@ -272,6 +274,11 @@ a { fill: #6b7280; } +#link_providers:hover * { + color: #111827; + stroke: #6b7280; +} + #link_my_deployments:hover * { color: #111827; stroke: #6b7280;