ProvisionerFlow.tsx 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. import React, { useState, useContext, useMemo } from "react";
  2. import styled from "styled-components";
  3. import { integrationList } from "shared/common";
  4. import { Context } from "shared/Context";
  5. import api from "shared/api";
  6. import ProvisionerForm from "components/ProvisionerForm";
  7. import CloudFormationForm from "components/CloudFormationForm";
  8. import CredentialsForm from "components/CredentialsForm";
  9. import Helper from "components/form-components/Helper";
  10. import AzureCredentialForm from "components/AzureCredentialForm";
  11. import AWSCostConsent from "./AWSCostConsent";
  12. import AzureCostConsent from "./AzureCostConsent";
  13. const providers = ["aws", "gcp", "azure"];
  14. type Props = {};
  15. const ProvisionerFlow: React.FC<Props> = ({}) => {
  16. const {
  17. usage,
  18. hasBillingEnabled,
  19. currentProject,
  20. featurePreview,
  21. } = useContext(Context);
  22. const [currentStep, setCurrentStep] = useState("cloud");
  23. const [credentialId, setCredentialId] = useState("");
  24. const [showCostConfirmModal, setShowCostConfirmModal] = useState(false);
  25. const [confirmCost, setConfirmCost] = useState("");
  26. const [useCloudFormationForm, setUseCloudFormationForm] = useState(true);
  27. const [selectedProvider, setSelectedProvider] = useState("");
  28. const isUsageExceeded = useMemo(() => {
  29. if (!hasBillingEnabled) {
  30. return false;
  31. }
  32. return usage?.current.clusters >= usage?.limit.clusters;
  33. }, [usage]);
  34. const markStepCostConsent = async (step: string, provider: string) => {
  35. try {
  36. await api.updateOnboardingStep("<token>", { step, provider }, {});
  37. } catch (err) {
  38. console.log(err);
  39. }
  40. };
  41. const openCostConsentModal = (provider: string) => {
  42. setSelectedProvider(provider);
  43. setShowCostConfirmModal(true);
  44. markStepCostConsent("cost-consent-opened", provider);
  45. };
  46. if (currentStep === "cloud") {
  47. return (
  48. <>
  49. <StyledProvisionerFlow>
  50. <Helper>Select your hosting backend:</Helper>
  51. <BlockList>
  52. {providers.map((provider: string, i: number) => {
  53. let providerInfo = integrationList[provider];
  54. return (
  55. <Block
  56. key={i}
  57. disabled={
  58. isUsageExceeded ||
  59. (provider === "azure" && !currentProject?.azure_enabled) ||
  60. provider === "gcp"
  61. }
  62. onClick={() => {
  63. if (
  64. !(
  65. isUsageExceeded ||
  66. (provider === "azure" && !currentProject?.azure_enabled) ||
  67. provider === "gcp"
  68. )
  69. ) {
  70. // openCostConsentModal(provider);
  71. setSelectedProvider(provider);
  72. setCurrentStep("credentials");
  73. }
  74. }}
  75. >
  76. <Icon src={providerInfo.icon} />
  77. <BlockTitle>{providerInfo.label}</BlockTitle>
  78. <BlockDescription>
  79. {(provider === "azure" && !currentProject?.azure_enabled) ||
  80. provider === "gcp" ? providerInfo.tagline : "Hosted in your own cloud"}
  81. </BlockDescription>
  82. </Block>
  83. );
  84. })}
  85. </BlockList>
  86. </StyledProvisionerFlow>
  87. {showCostConfirmModal &&
  88. ((selectedProvider === "aws" && (
  89. <AWSCostConsent
  90. setCurrentStep={setCurrentStep}
  91. setShowCostConfirmModal={setShowCostConfirmModal}
  92. markCostConsentComplete={() => {
  93. try {
  94. markStepCostConsent("cost-consent-complete", "aws");
  95. } catch (err) {
  96. console.log(err);
  97. }
  98. if (currentProject != null) {
  99. try {
  100. api.inviteAdmin(
  101. "<token>",
  102. {},
  103. { project_id: currentProject.id }
  104. );
  105. } catch (err) {
  106. console.log(err);
  107. }
  108. }
  109. }}
  110. />
  111. )) ||
  112. (selectedProvider === "azure" && (
  113. <AzureCostConsent
  114. setCurrentStep={setCurrentStep}
  115. setShowCostConfirmModal={setShowCostConfirmModal}
  116. markCostConsentComplete={() => {
  117. try {
  118. markStepCostConsent("cost-consent-complete", "azure");
  119. } catch (err) {
  120. console.log(err);
  121. }
  122. if (currentProject != null) {
  123. try {
  124. api.inviteAdmin(
  125. "<token>",
  126. {},
  127. { project_id: currentProject.id }
  128. );
  129. } catch (err) {
  130. console.log(err);
  131. }
  132. }
  133. }}
  134. />
  135. )))}
  136. </>
  137. );
  138. } else if (currentStep === "credentials") {
  139. return (
  140. (selectedProvider === "aws" &&
  141. (useCloudFormationForm ? (
  142. <CloudFormationForm
  143. goBack={() => setCurrentStep("cloud")}
  144. proceed={(id) => {
  145. setCredentialId(id);
  146. setCurrentStep("cluster");
  147. }}
  148. switchToCredentialFlow={() => setUseCloudFormationForm(false)}
  149. />
  150. ) : (
  151. <CredentialsForm
  152. goBack={() => setCurrentStep("cloud")}
  153. proceed={(id) => {
  154. setCredentialId(id);
  155. setCurrentStep("cluster");
  156. }}
  157. />
  158. ))) ||
  159. (selectedProvider === "azure" && (
  160. <AzureCredentialForm
  161. goBack={() => setCurrentStep("cloud")}
  162. proceed={(id) => {
  163. setCredentialId(id);
  164. setCurrentStep("cluster");
  165. }}
  166. />
  167. ))
  168. );
  169. } else if (currentStep === "cluster") {
  170. return (
  171. <ProvisionerForm
  172. goBack={() => setCurrentStep("credentials")}
  173. credentialId={credentialId}
  174. provider={selectedProvider}
  175. />
  176. );
  177. }
  178. };
  179. export default ProvisionerFlow;
  180. const Cost = styled.div`
  181. font-weight: 600;
  182. font-size: 20px;
  183. `;
  184. const Tab = styled.span`
  185. margin-left: 20px;
  186. height: 1px;
  187. `;
  188. const BlockList = styled.div`
  189. overflow: visible;
  190. margin-top: 25px;
  191. margin-bottom: 27px;
  192. display: grid;
  193. grid-column-gap: 25px;
  194. grid-row-gap: 25px;
  195. grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  196. `;
  197. const Icon = styled.img<{ bw?: boolean }>`
  198. height: 30px;
  199. margin-top: 30px;
  200. margin-bottom: 15px;
  201. filter: ${(props) => (props.bw ? "grayscale(1)" : "")};
  202. `;
  203. const BlockDescription = styled.div`
  204. margin-bottom: 12px;
  205. color: #ffffff66;
  206. text-align: center;
  207. font-weight: 400;
  208. font-size: 13px;
  209. padding: 0px 25px;
  210. height: 2.4em;
  211. font-size: 12px;
  212. display: -webkit-box;
  213. overflow: hidden;
  214. -webkit-line-clamp: 2;
  215. -webkit-box-orient: vertical;
  216. `;
  217. const BlockTitle = styled.div`
  218. margin-bottom: 12px;
  219. width: 80%;
  220. text-align: center;
  221. font-size: 14px;
  222. white-space: nowrap;
  223. overflow: hidden;
  224. text-overflow: ellipsis;
  225. `;
  226. const Block = styled.div<{ disabled?: boolean }>`
  227. align-items: center;
  228. user-select: none;
  229. display: flex;
  230. font-size: 13px;
  231. overflow: hidden;
  232. font-weight: 500;
  233. padding: 3px 0px 5px;
  234. flex-direction: column;
  235. align-items: center;
  236. justify-content: space-between;
  237. height: 170px;
  238. filter: ${({ disabled }) => (disabled ? "brightness(0.8) grayscale(1)" : "")};
  239. cursor: ${(props) => (props.disabled ? "not-allowed" : "pointer")};
  240. color: #ffffff;
  241. position: relative;
  242. border-radius: 5px;
  243. background: ${({ theme }) => theme.clickable.bg};
  244. border: 1px solid #494b4f;
  245. :hover {
  246. border: ${(props) => (props.disabled ? "" : "1px solid #7a7b80")};
  247. }
  248. animation: fadeIn 0.3s 0s;
  249. @keyframes fadeIn {
  250. from {
  251. opacity: 0;
  252. }
  253. to {
  254. opacity: 1;
  255. }
  256. }
  257. `;
  258. const StyledProvisionerFlow = styled.div`
  259. margin-top: -24px;
  260. `;