From efee8f99c8f7fd1b5063890e742759151fa33e23 Mon Sep 17 00:00:00 2001 From: Arthur Emmanuel <86601468+ArthurEmma2@users.noreply.github.com> Date: Fri, 14 Jul 2023 21:27:18 +0000 Subject: [PATCH 01/11] Feat: import SDL functionality on landing page added --- package.json | 8 +- web/craco.config.js | 2 +- web/src/App.tsx | 6 +- web/src/Landing-Metadata/landing.ts | 110 + .../Landing-Metadata/landingIcons/chip.png | Bin 0 -> 1404 bytes .../Landing-Metadata/landingIcons/code.png | Bin 0 -> 1301 bytes .../landingIcons/first_img.png | Bin 0 -> 1487 bytes web/src/Landing-Metadata/landingIcons/www.png | Bin 0 -> 1858 bytes web/src/components/Deployment/index.tsx | 16 +- .../components/DeploymentStepper/index.tsx | 106 +- web/src/components/MonacoYamlEditor/index.tsx | 1 + .../SdlConfiguration/SdlConfiguration.tsx | 3 + .../SdlConfiguration/SdllEditor.tsx | 7 + web/src/components/TileCard/TileCard.css | 166 + web/src/components/TileCard/TileCard.tsx | 307 ++ web/src/pages/ConfigureApp.tsx | 6 + web/src/pages/CustomApp.tsx | 48 +- web/src/pages/Deploy.tsx | 2 +- web/src/pages/FeaturedApps.tsx | 40 +- web/src/pages/Landing.tsx | 24 + web/src/pages/ReDeploy.tsx | 16 +- web/src/pages/UpdateDeployment.tsx | 152 +- web/src/style/landing.css | 84 + web/tailwind.config.js | 58 +- web/tsconfig.json | 16 +- yarn.lock | 4534 +++++++++-------- 26 files changed, 3354 insertions(+), 2358 deletions(-) create mode 100644 web/src/Landing-Metadata/landing.ts create mode 100644 web/src/Landing-Metadata/landingIcons/chip.png create mode 100644 web/src/Landing-Metadata/landingIcons/code.png create mode 100644 web/src/Landing-Metadata/landingIcons/first_img.png create mode 100644 web/src/Landing-Metadata/landingIcons/www.png create mode 100644 web/src/components/TileCard/TileCard.css create mode 100644 web/src/components/TileCard/TileCard.tsx create mode 100644 web/src/pages/Landing.tsx create mode 100644 web/src/style/landing.css diff --git a/package.json b/package.json index ed9349c..4fe0d84 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,11 @@ "web" ], "dependencies": { - "concurrently": "^7.2.1" + "concurrently": "^7.2.1", + "path": "^0.12.7" }, - "version": "0.1.292" + "version": "0.1.292", + "devDependencies": { + "url-loader": "^4.1.1" + } } diff --git a/web/craco.config.js b/web/craco.config.js index 461fd75..30effce 100644 --- a/web/craco.config.js +++ b/web/craco.config.js @@ -52,4 +52,4 @@ module.exports = { ], }, }, -}; +}; \ No newline at end of file diff --git a/web/src/App.tsx b/web/src/App.tsx index 909c907..3c20739 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -12,6 +12,7 @@ import { loadActiveCertificate } from './recoil/api'; import { useWallet } from './hooks/useWallet'; import Loading from './components/Loading'; + // Lazy loading all pages in appropriate time const DeploymentStepper = lazy(() => import('./components/DeploymentStepper')); const Deployment = lazy(() => import('./components/Deployment')); @@ -21,12 +22,14 @@ const MyDeployments = lazy(() => import('./pages/MyDeployments')); const UpdateDeployment = lazy(() => import('./pages/UpdateDeployment')); const CustomApp = lazy(() => import('./pages/CustomApp')); const Provider = lazy(() => import('./pages/Provider')); +import Landing from './pages/Landing'; + const Welcome = () => { const navigate = useNavigate(); useEffect(() => { - navigate('/landing/node-deployment'); + navigate('/landing'); }, []); return <>; @@ -44,6 +47,7 @@ const AppRouter = () => {
+ } /> } /> }/> diff --git a/web/src/Landing-Metadata/landing.ts b/web/src/Landing-Metadata/landing.ts new file mode 100644 index 0000000..f4e716d --- /dev/null +++ b/web/src/Landing-Metadata/landing.ts @@ -0,0 +1,110 @@ +import img1 from './landingIcons/first_img.png'; +import img2 from './landingIcons/www.png'; +import img3 from './landingIcons/chip.png'; + +interface Tile { + title: string; + description: string; + image: string; + buttonText: string; + route: string; + icon: string; + buttonEnabled: boolean; + onClick?: (setFieldValue: (name: string, value: any) => void, showSdlReview: (value: boolean) => void) => void; + + } + + +interface CategoryTiles { + introText: string; + tiles: Tile[]; +} + +interface Metadata { + version: string; + categoriesTiles: CategoryTiles; + sdlGuideTiles: { + introText: string; + introDescription: string; + tiles: { + step: string; + text: string; + image: string; + }[]; + }; +} + +export const metadata: Metadata = { + version: '0.0.1', + categoriesTiles: { + introText: 'What would you like to do today?', + tiles: [ + { + title: 'Deploy a Blockchain Node', + description: + 'Easy and low cost hosting for your blockchain nodes (RPC servers, Validators and more)', + buttonText: 'Choose a Template', + route: '/landing/node-deployment', + icon: 'xrayView', + image: img1, + buttonEnabled: true, + }, + { + title: 'Host a Website or Web Service', + description: + 'Low cost, decentralized equivalents of the services provided by mainstream cloud providers. Host websites, blogsites, databases and more.', + buttonText: 'Coming Soon', + route: '', + icon: 'www', + image: img2, + buttonEnabled: false, + }, + { + title: 'Deploy an AI/ ML Model', + description: + 'Popular AI & ML models, deployed in just a few clicks. Includes Stable Diffusion, GPT4All, Alpaca and more.', + buttonText: 'Coming Soon', + route: '', + icon: 'electronicsChip', + image: img3, + buttonEnabled: false, + }, + { + 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', + route: '', + icon: 'electronicsChip', + image: img3, + buttonEnabled: true, + onClick: (setFieldValue: (name: string, value: any) => void, showSdlReview: (value: boolean) => void) => { + setFieldValue('sdl', {}); + showSdlReview(true); + }, + }, + ], + }, + sdlGuideTiles: { + introText: 'How it works?', + introDescription: + 'There are 3 main steps to deploying on Akash. Check out our detailed help for more.', + tiles: [ + { + step: '01', + text: 'Start with a template or your own custom application (SDL)', + image: './landingIcons/code.png', + }, + { + step: '02', + text: 'Choose a provider based on your preferences and desired price', + image: './landingIcons/code.png', + }, + { + step: '03', + text: 'View & manage your deployed application', + image: '/template-icons/landing/illustrations/deploy-app.png', + }, + ], + }, +}; diff --git a/web/src/Landing-Metadata/landingIcons/chip.png b/web/src/Landing-Metadata/landingIcons/chip.png new file mode 100644 index 0000000000000000000000000000000000000000..8286a7e0a5d01ece9df6105ac95e72ed30e65d3b GIT binary patch literal 1404 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 LNkvXXu0mjfB+Xr8 literal 0 HcmV?d00001 diff --git a/web/src/Landing-Metadata/landingIcons/first_img.png b/web/src/Landing-Metadata/landingIcons/first_img.png new file mode 100644 index 0000000000000000000000000000000000000000..17836294ff7f94f81e6852f3c3db1273de167391 GIT binary patch literal 1487 zcmV;=1u*)FP)qr#G4^E)s5uOqSY;F&_P76rM{Q|)4pt@`Y5=iVeAVC2sK!RpL0*P+#R?zJNAK-!n zb+UpUFBFJ}M4&_j_WTDsa)KQj&zPJ1q>%?dhWY25IX*Mv8dA*J*;%ZCVnhg8~vrV93IpSY)Ap1rhpgfmc^o!_jE;4|+SM z!p6ww^Xd1xu5W>+)fVX0YCn@TP1DlN&CNMKH~SKJd3hPHtE>B)t2$K%2D|JRKQ8Mj z&=mKBKHeq0@FlXWzzP;;;VIHpU=@h7C@a!YpczmyRQciroLpqNd~pgqn$rVjw2V`g zz}b(^+0TO0wKo>LzP=udL?VAu6~aQlwzakWV?S3Uu!_c=8##VQezLo2k&CjpZxoWa z?d@$!Bod;a9ul#qr>CMsq+mhQdS|4VTBJKf#gw6U>4{r&xQozH*1I^FRldkJ@a&kg5 zGc$B}cu1q8qlJ4=&t|jY+R@RGxL2i6w&3*L-JO`_2!V*4BsumVY;|>2barcNi^j*t zg^1bNS!!x(a(o8Y_V)H@WMo8)OIQ?bC?Li!{}d`hAS)gw3DCJ@GAYJdPfrhJG8tN4 zUUn5`2V-$_b5lIi*ViZ7P!WiD$YH+vDln*fTH_4v)z#HSySux>BIxvcv4o-xj8C)) zp+VgiFMqhHc5`!+qJXpV$;n9(9U%^#8yFZMSOSkOBXcpwufWCD~ISi*$B zPoxO9Kv9~-0Gv}5pxod-#G4nKF`>j2%b`)oAb_x!h@(X;78B>f76(EVSQUvs z;}$?rht>QBA_W+D58ja?oTf++=9J-Bfhf$W6O0E7{!5bhnIgFn^3*98Wr0|7`$hpd zJv|kjLS#e+Qf_)w4m@X%xNuptDU;1$uMt5eO98RxiYhV?dqxB#^H7l0cqm-3pppXer9*6~Cv22K$FN?&Iq4ah%etiUrot+)=>!YLSG=OygECXSf8llJrs8ZM? zjh2=cF^LcsgqzA*(ZbKvDaIAHJUPz={TU!^cKZ*pxifrBtmpD}+7<=YEp<$gXnoXEv@pr0HxbJDZ zIPhk=g#9(Pppt;|!K`!Nzn7kL+`=|p^#>MDsR}S%GvRv++stmh;z6o#Y~#)`-7NbV z30pF5S~5-*h*MB`bB{+G6)Vz@IN-S^g9gx0CAq8Zvk}AVUSEq&rpQEvMAvn{;=sE< zr@Xzr9YePB(JsrP@Q(hq^fM99=O_Rem;BGhxEe}YX9vRIIsBup`Tc~l=y{R=Li71N z^4Fwli^>Wsv_p!3>+)gP6dPzpV!9z#`PL$l$bpQoLYou<*fnOMItxwlpE2%uoR6iO pJXmnfnX7tP7M8{OkP10r!N2i literal 0 HcmV?d00001 diff --git a/web/src/Landing-Metadata/landingIcons/www.png b/web/src/Landing-Metadata/landingIcons/www.png new file mode 100644 index 0000000000000000000000000000000000000000..68989be75057bfa23b9602e9703e0d4d56441dbd GIT binary patch literal 1858 zcmV-I2fg@-P)bMK~#7F)tfzR z6H5@sCni7xB;iY<0FoXgy9J~;B_Q!A0SOT*Kp?%)fCL3lP(btYBoI&$1QK+7NI)4! zf;xGEniwb$-w7q+ep+pmVU3=4CUv~pH;p7$5l&Z?b@|dk~6v_CqQi3vSq_c=r1j02}e~+<;Ra7 zH`c6KQ;{5yndJNT@5@T+Z!Nnee^kGJ|9(!jJ0=+rO!Cc}H~otjFTS2pYe|ubY!Jzh zA3vVa%ZU7%XMYmauV23oty{OQnx;`Il4x(qk|lTbXHn)CXIHISbuMY;#3Y$}g+c*> ze_DIZFsek4C#jf-#_i#B+KgY|7rF-mL$=OdeL#|94C_mBkCMS;vU43T(M#W z>tMeuFsS!Gg9XmHAH7klG$L)n>({U4%a~nc=4ipwtoG3M}EVG4UXucLx-fNr$@3t|F6o*l`Dtpvj0%EI4K!J zHneu^*fDwa>Xp+Fq#(F!*Dkqq=~7)|7cN|IL?1kOfOnMJw{OdxJ9iw}n>TMdEoXtR z%{s|i*fuU1aP8VPId$rk(+-5TZQJJVLlDwaQ;qxKVzKB*K@?Djx@h*~$&+q?Lg8z( z;N2XS3_vRo+`fIgOiWDF2|j=R+-a9iM}by(Mgb6}?T#HgoTf4NP_TA>{`}c3fj-GW z2?Rvw6a;VGy45hJtfQE26o(HVc6D)124u_?qNh)vt`9&6zJ2>P%+;A|7E{HE3=R&u zd4(3DPNP&R$(Ai!c}6{o58lPV(Ei!8XA%P9T1?hyQg<5T?Mh?N`7BF9*7lkCDX)k=E`>Sp^TC2;S+v=w({$-vaOnsY*9 zbT%rpURqrvL97d+a@t5poljpu0$?42jCIt=nQQ~$4~TU%D%%=LT}Yu-8!%snAOS;4 z^ahc%j?n>I$O!~|**Z?A2=nM9@hKgR%GPlk5TYmEwP(+s2HBpcMM2d$BLT3mji`i9 zA31WQL1qhOFbz>4AC(O@Adm+{M7|tMQd9jwByG;w@2Kp<4+iLHRQ7>vTQCMHDyM}y zo@vZAh$Jv`vh7n;HrN2rQ7j-T`>+A9fr`p$pM6 zS2RRJQSglRaghvGLHp1E9grQcC8Q#OuqA<%N8C<=@;j2WkH)BIm&k}`0c)d!DsO_T zBU#IZ1UVpoO9as`;6=nO&n82{9R0cCwd0zoIwpFi)&Sj=j$ z{bF1y!y+EmLjxM*9lQ(Y6tZj>H1k;DnrkAZ@$*n9n}heQ{1H-{{MgZBTRn(COzb4cc?%{W-nDs)*$fH7TL5BfJ^Kc{O1G|B-a zSzypU&uA%Ws9N>!$L=T9^`6D9Wr0D>^jwr6h$TtT&S3+|utS*#{Hta<8uy^(>5q2L z&%D415cQ^|X3txuKUh2QRMNq)BQ7oLNCK!M;)rL*odijGSSe8=i#3OIdxtrr8%YR) zR0jx2M63A{EcDC#;&?MB6NgkON!)WSTuOXGF;5V5t0OF>AqavG^`ccZVb;?F(q^#8 zvj(OmS8p#F(N41X0QR}6z0o9dujzXvC)CK6_2+PwPxEMvL-tDUUdJ4?j{{$KA = () => { const { dseq } = useParams(); @@ -271,8 +271,8 @@ const Deployment: React.FC = () => { )} {deployment?.deployment && !deploymentIncomplete && ( - = () => { aria-controls="menu-appbar" aria-haspopup="true" startIcon={} - > + Update Deployment - + - = () => { aria-controls="menu-appbar" aria-haspopup="true" startIcon={} - > Update Deployment - + = () => { color="secondary" aria-label="re-deploy" sx={{ - justifyContent: 'left' + justifyContent: 'left', }} startIcon={} > diff --git a/web/src/components/DeploymentStepper/index.tsx b/web/src/components/DeploymentStepper/index.tsx index 7444dc5..62d7559 100644 --- a/web/src/components/DeploymentStepper/index.tsx +++ b/web/src/components/DeploymentStepper/index.tsx @@ -55,7 +55,8 @@ const DeploymentStepper: React.FC = () => { const [errorMessage, setErrorMessage] = React.useState(); const [myDeployments, setMyDeployments] = useRecoilState(myDeploymentsAtom); const [, setDeploymentRefresh] = useRecoilState(deploymentDataStale); - const { mutate: mxCreateDeployment, isLoading: deploymentProgressVisible } = useMutation(createDeployment); + const { mutate: mxCreateDeployment, isLoading: deploymentProgressVisible } = + useMutation(createDeployment); const { mutate: mxCreateLease, isLoading: leaseProgressVisible } = useMutation(createLease); const { mutate: mxSendManifest, isLoading: manifestSending } = useMutation(sendManifest); @@ -115,26 +116,29 @@ const DeploymentStepper: React.FC = () => { const _sdl = sdl ? sdl : cachedDetails.sdl; if (lease) { - mxSendManifest({ address: keplr.accounts[0].address, lease, sdl: _sdl}, { - onSuccess: (result) => { + mxSendManifest( + { address: keplr.accounts[0].address, lease, sdl: _sdl }, + { + onSuccess: (result) => { if (result) { logging.success('Manifest sending: successful'); setDeploymentRefresh(true); navigate(`/my-deployments/${dseq}`); } }, - onError: (error) => { - logging.log(`Failed to send manifest: ${error}`); - }, - onSettled: () => { - navigate(`/my-deployments/${dseq}`); + onError: (error) => { + logging.log(`Failed to send manifest: ${error}`); + }, + onSettled: () => { + navigate(`/my-deployments/${dseq}`); + }, } - }); + ); } }, onError: (error) => { logging.log(`Failed to create lease: ${error}`); - } + }, }); }; @@ -185,7 +189,8 @@ const DeploymentStepper: React.FC = () => { setCardMessage('Creating deployment'); try { - const result = mxCreateDeployment({ sdl: value.sdl, depositor: value.depositor }, + const result = mxCreateDeployment( + { sdl: value.sdl, depositor: value.depositor }, { onSuccess: async (result) => { if (result && result.deploymentId) { @@ -202,7 +207,7 @@ const DeploymentStepper: React.FC = () => { // head to the bid selection page navigate(`/configure-deployment/${result.deploymentId.dseq}`); } - } + }, } ); } catch (error) { @@ -251,43 +256,48 @@ const DeploymentStepper: React.FC = () => { {activeStep.currentCard === steps.length ? null : !progressVisible && ( - - {activeStep.currentCard === 0 && ( - { - selectFolder(folderName); - }} - callback={(sdl) => - navigate('/new-deployment/custom-sdl', { state: { sdl: sdl } }) - } - setFieldValue={setFieldValue} - /> - )} - {activeStep.currentCard === 1 && folderName && ( - - )} - {activeStep.currentCard === 2 && folderName && templateId && ( - handlePreflightCheck(intent, values.sdl)} - /> - )} - {activeStep.currentCard === 3 && } - {activeStep.currentCard === 4 && deploymentId && ( - }> - acceptBid(bidId)} + + {activeStep.currentCard === 0 && ( + { + selectFolder(folderName); + }} + callback={(sdl) => + navigate('/new-deployment/custom-sdl', { state: { sdl: sdl } }) + } + setFieldValue={setFieldValue} + onSave={function (sdl: any): void { + throw new Error('Function not implemented.'); + }} + /> + )} + {activeStep.currentCard === 1 && folderName && ( + + )} + {activeStep.currentCard === 2 && folderName && templateId && ( + + handlePreflightCheck(intent, values.sdl) + } /> - - )} - - )} + )} + {activeStep.currentCard === 3 && } + {activeStep.currentCard === 4 && deploymentId && ( + }> + acceptBid(bidId)} + /> + + )} + + )} ); }} diff --git a/web/src/components/MonacoYamlEditor/index.tsx b/web/src/components/MonacoYamlEditor/index.tsx index 238a24c..e61d3b5 100644 --- a/web/src/components/MonacoYamlEditor/index.tsx +++ b/web/src/components/MonacoYamlEditor/index.tsx @@ -18,6 +18,7 @@ interface MonacoYamlEditorProps { closeReviewModal: () => void; onSaveButtonClick: (value: SDLSpec) => void; disabled: boolean; + onSave: (sdl: any) => void; // Add onSave prop } export const MonacoYamlEditor: React.FC = ({ diff --git a/web/src/components/SdlConfiguration/SdlConfiguration.tsx b/web/src/components/SdlConfiguration/SdlConfiguration.tsx index 1ae7add..6ae67cd 100644 --- a/web/src/components/SdlConfiguration/SdlConfiguration.tsx +++ b/web/src/components/SdlConfiguration/SdlConfiguration.tsx @@ -37,6 +37,7 @@ interface SdlConfigurationProps { configurationType: SdlConfigurationType; progressVisible?: boolean; cardMessage?: string | undefined; + onSave: (sdl: any) => void; } export const SdlConfiguration: React.FC = ({ @@ -47,6 +48,7 @@ export const SdlConfiguration: React.FC = ({ configurationType, progressVisible, cardMessage, + onSave, }) => { const forbidEditing = configurationType === SdlConfigurationType.Update; @@ -200,6 +202,7 @@ export const SdlConfiguration: React.FC = ({ reviewSdl={reviewSdl} closeReviewModal={closeReviewModal} disabled={forbidEditing} + onSave={onSave} // Pass the onSave prop /> diff --git a/web/src/components/SdlConfiguration/SdllEditor.tsx b/web/src/components/SdlConfiguration/SdllEditor.tsx index a7f8bef..6178a35 100644 --- a/web/src/components/SdlConfiguration/SdllEditor.tsx +++ b/web/src/components/SdlConfiguration/SdllEditor.tsx @@ -9,6 +9,7 @@ interface SdlEditorProps { closeReviewModal: () => void; disabled?: boolean; callback?: (sdl: any) => void; + onSave: (sdl: any) => void; } export const SdlEditor: React.FC = ({ @@ -16,6 +17,7 @@ export const SdlEditor: React.FC = ({ closeReviewModal, disabled, callback, + onSave, // Add the onSave prop }) => { return ( @@ -35,8 +37,13 @@ export const SdlEditor: React.FC = ({ if (callback) { return callback(value); } + + if (onSave) { + return onSave(value); + } form.setFieldValue(field.name, value); }} + onSave={onSave} /> ); }} diff --git a/web/src/components/TileCard/TileCard.css b/web/src/components/TileCard/TileCard.css new file mode 100644 index 0000000..6516446 --- /dev/null +++ b/web/src/components/TileCard/TileCard.css @@ -0,0 +1,166 @@ +.tile_card { + border-radius: 8px; + border: 1px solid rgba(0, 0, 0, 0.1); + background: var(--white, #fff); + width: 305px; + height: 318px; +} + + +.tile_card_container.gap-4 > div:nth-child(2) > div > div > p{ + width: 273.75px; + height: 96px; + +} + + +.tile-card_title{ +color: var(--gray-900, #111827); +font-size: 18px; +font-style: normal; +font-weight: 700; +line-height: 22px; +margin-top: 10px; +} + +.tile_wrapper{ + gap: 16px; +} + +.tile-card_desc{ +color: rgba(0, 0, 0, 0.50); +font-size: 16px; +font-style: normal; +font-weight: 500; +line-height: 24px; /* 150% */ +margin-top: 30px; +} + +.tile_btn{ + margin-top: 24px; +border-radius: 6px; +border: 1px solid var(--slate-200, #D8D7D7); +background: #FFF; +width: 252.75px; +height: 40px; +padding: 8px 16px 8px 16px; +color: #0F172A; +font-size: 14px; +font-style: normal; +font-weight: 500; +line-height: 24px; +margin: 10px auto !important; +margin-top: 24px !important; +} + + div:nth-child(2) > div:nth-child(1) > div > div > button{ + margin-top: 70px !important; +} + + div:nth-child(2) > div:nth-child(4) > div > div > button{ + margin-top: 50px !important; + } + +div:nth-child(2) > div:nth-child(3) > div > div > button{ + margin-top: 50px !important; +} + +div:nth-child(2) > div:nth-child(1) > div > div > div > p{ + width: 142px; +height: 44px; + +} + +div:nth-child(2) > div:nth-child(2) > div > div > div > p{ + width: 153px; + height: 44px; + +} + + div:nth-child(2) > div:nth-child(3) > div > div > div > p{ + width: 113px; +height: 44px; + + +} + + + div:nth-child(2) > div:nth-child(4) > div > div > div > p{ + width: 96px; +height: 44px; + +} + + +@media (max-width: 768px) { + .tile-card_wrapper { + display: grid; + grid-template-columns: repeat(2, 1fr); + } + + .tile_card{ + width: 100%; + height: 100%; + } + } + + @media (max-width: 480px) { + .tile-card_wrapper { + display: grid; + grid-template-columns: 1fr; + } + + + .tile_card{ + width: 100%; + height: 100%; + } + + .tile_btn{ + width: 100%; + } + } + + @media (min-width: 478px) and (max-width: 701px) { + + .tile_card{ + width: 100%; + height: 100%; + } + + .tile_btn{ + width: 100%; + } + } + + + @media screen and (min-width: 1440px) { + .tile_card { + border-radius: 8px; + border: 1px solid rgba(0, 0, 0, 0.1); + background: var(--white, #fff); + width: 330px; + height: 318px; + } + + .tile_btn{ + width: 282.75px; + height: 40px; + padding: 8px 16px 8px 16px; + color: #0F172A; + } + + div:nth-child(2) > div:nth-child(4) > div > div > button{ + margin-top: 50px !important; + } + + div:nth-child(2) > div:nth-child(3) > div > div > button{ + margin-top: 75px !important; + } + +.tile-card_wrapper > div:nth-child(2) > div > div > button{ + margin-top: 50px !important; +} + } + + \ No newline at end of file diff --git a/web/src/components/TileCard/TileCard.tsx b/web/src/components/TileCard/TileCard.tsx new file mode 100644 index 0000000..124975b --- /dev/null +++ b/web/src/components/TileCard/TileCard.tsx @@ -0,0 +1,307 @@ +import React, { Suspense, useState, useCallback } from 'react'; +import { useRecoilState, useRecoilValue } from 'recoil'; +import { SdlEditor } from '../../components/SdlConfiguration/SdllEditor'; +import { useNavigate, useParams } from 'react-router-dom'; +import { + Box, + Card, + CardContent, + Slide, + Stack, + Step, + StepLabel, + Stepper, + Typography, +} from '@mui/material'; +import { Formik } from 'formik'; +import { + deploymentDataStale, + deploymentSdl, + keplrState, + myDeployments as myDeploymentsAtom, +} from '../../recoil/atoms'; +import './TileCard.css'; +interface Props { + item: { + title: string; + image: string; + description: string; + buttonText: string; + }; +} + +import { Dialog } from '../Dialog'; +import FeaturedApps from '../../pages/FeaturedApps'; +import SelectApp from '../../pages/SelectApp'; +import SelectProvider from '../../pages/SelectProvider'; +import { ConfigureApp } from '../../pages/ConfigureApp'; +import { PreflightCheck } from '../../pages/PreflightCheck'; +import { nameToURI, uriToName } from '../../_helpers/param-helpers'; +import { initialValues, InitialValuesProps, SDLSpec } from '../SdlConfiguration/settings'; +import { myDeploymentFormat } from '../../_helpers/my-deployment-utils'; +import logging from '../../logging'; +import Loading from '../Loading'; +import { Deployment } from '@akashnetwork/akashjs/build/protobuf/akash/deployment/v1beta2/deployment'; +import { useMutation } from 'react-query'; +import { createDeployment, createLease, sendManifest } from '../../api/mutations'; +import { getRpcNode } from '../../hooks/useRpcNode'; + +const steps = ['Featured Apps', 'Select', 'Configure', 'Review', 'Deploy']; + +export interface DeploymentStepperProps { + dseq?: string; + leaseId?: string; +} + +function TileCard(props: Props) { + const { title, image, description, buttonText } = props.item; + 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 [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 [, setDeploymentRefresh] = useRecoilState(deploymentDataStale); + const { mutate: mxCreateDeployment, isLoading: deploymentProgressVisible } = + useMutation(createDeployment); + const { mutate: mxCreateLease, isLoading: leaseProgressVisible } = useMutation(createLease); + const { mutate: mxSendManifest, isLoading: manifestSending } = useMutation(sendManifest); + + const progressVisible = deploymentProgressVisible || leaseProgressVisible || manifestSending; + + React.useEffect(() => { + const params = [folderName, templateId && uriToName(templateId), intentId]; + const numParams = params.filter((x) => x !== undefined).length; + + setActiveStep({ currentCard: numParams }); + }, [folderName, templateId, intentId]); + + React.useEffect(() => { + if (dseq) { + setDeploymentId({ + owner: keplr.accounts[0].address, + dseq, + }); + setActiveStep({ currentCard: 4 }); + return; + } + }, [dseq, keplr]); + + const selectFolder = (folderName: string) => { + navigate(`/new-deployment/${nameToURI(folderName)}`); + }; + + const selectTemplate = (templateId: string) => { + if (folderName) { + navigate(`/new-deployment/${nameToURI(folderName)}/${nameToURI(templateId)}`); + } + }; + + const handlePreflightCheck = (intentId: string, sdl: SDLSpec | undefined) => { + if (folderName && templateId && sdl) { + setSdl(sdl); + navigate(`/new-deployment/${nameToURI(folderName)}/${nameToURI(templateId)}/${intentId}`); + } + }; + + const handleSdlEditorSave = (sdl: any) => { + navigate('/new-deployment/custom-sdl', { state: { sdl: sdl } }); + }; + + const handleDeployment = (key: string, deployment: any) => { + const newDeployments: { [key: string]: Deployment } = { ...myDeployments }; + newDeployments[key] = deployment; + setMyDeployments(newDeployments); + }; + + const [reviewSdl, setReviewSdl] = useState(false); + const closeReviewModal = useCallback(() => setReviewSdl(false), []); + + const handleImportSDL = () => { + setReviewSdl(true); + }; + + const acceptBid = async (bidId: any) => { + setCardMessage('Deploying'); + + mxCreateLease(bidId, { + onSuccess: (lease) => { + logging.success('Create lease: successful'); + + // if the user refreshed the page, the atom could be empty + // if that's the case, used the stored version + const cachedDetails = JSON.parse(localStorage.getItem(`${lease?.leaseId?.dseq}`) || ''); + const _sdl = sdl ? sdl : cachedDetails.sdl; + + if (lease) { + mxSendManifest( + { address: keplr.accounts[0].address, lease, sdl: _sdl }, + { + onSuccess: (result) => { + if (result) { + logging.success('Manifest sending: successful'); + setDeploymentRefresh(true); + navigate(`/my-deployments/${dseq}`); + } + }, + onError: (error) => { + logging.log(`Failed to send manifest: ${error}`); + }, + onSettled: () => { + navigate(`/my-deployments/${dseq}`); + }, + } + ); + } + }, + onError: (error) => { + logging.log(`Failed to create lease: ${error}`); + }, + }); + }; + + // Error handling dialog + + // 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); + setCardMessage(''); + setOpen(true); + throw new Error(`${method}: ${error.message}`); + }; + + return ( + + { + // the onSubmit method is called from the component PreflightCheck. + // it uses the useFormikContext hook. + // const { submitForm } = useFormikContext(); + setCardMessage('Creating deployment'); + + try { + const result = mxCreateDeployment( + { sdl: value.sdl, depositor: value.depositor }, + { + onSuccess: async (result) => { + if (result && result.deploymentId) { + setDeploymentId(result.deploymentId); + setSdl(value.sdl); + + // 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)); + + // head to the bid selection page + navigate(`/configure-deployment/${result.deploymentId.dseq}`); + } + }, + } + ); + } catch (error) { + await handleError(error, 'createDeployment'); + } + }} + > + {({ setFieldValue, values }) => ( + <> +
+
+
+
+ {title} +

{title}

+
+

{description}

+ +
+
+
+ + {activeStep.currentCard === steps.length + ? null + : !progressVisible && ( + + {activeStep.currentCard === 0 && ( + + )} + + + {activeStep.currentCard === 1 && folderName && ( + + )} + {activeStep.currentCard === 2 && folderName && templateId && ( + + handlePreflightCheck(intent, values.sdl) + } + /> + )} + {activeStep.currentCard === 3 && } + {activeStep.currentCard === 4 && deploymentId && ( + }> + acceptBid(bidId)} + /> + + )} + + )} + + )} +
+
+ ); +} + +export default TileCard; diff --git a/web/src/pages/ConfigureApp.tsx b/web/src/pages/ConfigureApp.tsx index 1dd91c2..85a774f 100644 --- a/web/src/pages/ConfigureApp.tsx +++ b/web/src/pages/ConfigureApp.tsx @@ -37,6 +37,11 @@ export const ConfigureApp: React.FC = ({ keepPreviousData: true, }); + const handleSave = (sdl: any) => { + // Update the form values with the saved SDL + form.setFieldValue('sdl', sdl); + }; + useEffect(() => { // don't override the value if it's already set if (form.values?.sdl?.version) return; @@ -70,6 +75,7 @@ export const ConfigureApp: React.FC = ({ configurationType={SdlConfigurationType.Create} progressVisible={progressVisible} cardMessage={cardMessage} + onSave={handleSave} // Add the onSave prop actionItems={() => (