CreateDatabase.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. import React, { useMemo } from "react";
  2. import _ from "lodash";
  3. import { withRouter, type RouteComponentProps } from "react-router";
  4. import styled from "styled-components";
  5. import { match } from "ts-pattern";
  6. import { z } from "zod";
  7. import Back from "components/porter/Back";
  8. import Spacer from "components/porter/Spacer";
  9. import Tag from "components/porter/Tag";
  10. import Text from "components/porter/Text";
  11. import database from "assets/database.svg";
  12. import DashboardHeader from "../cluster-dashboard/DashboardHeader";
  13. import { SUPPORTED_DATABASE_TEMPLATES } from "./constants";
  14. import DatabaseFormAuroraPostgres from "./forms/DatabaseFormAuroraPostgres";
  15. import DatabaseFormElasticacheRedis from "./forms/DatabaseFormElasticacheRedis";
  16. import DatabaseFormRDSPostgres from "./forms/DatabaseFormRDSPostgres";
  17. import {
  18. DATABASE_ENGINE_POSTGRES,
  19. DATABASE_ENGINE_REDIS,
  20. DATABASE_TYPE_AURORA,
  21. DATABASE_TYPE_ELASTICACHE,
  22. DATABASE_TYPE_RDS,
  23. type DatabaseTemplate,
  24. } from "./types";
  25. type Props = RouteComponentProps;
  26. const CreateDatabase: React.FC<Props> = ({ history, match: queryMatch }) => {
  27. const templateMatch: DatabaseTemplate | undefined = useMemo(() => {
  28. const { params } = queryMatch;
  29. const validParams = z
  30. .object({
  31. type: z.string(),
  32. engine: z.string(),
  33. })
  34. .safeParse(params);
  35. if (!validParams.success) {
  36. return undefined;
  37. }
  38. return SUPPORTED_DATABASE_TEMPLATES.find(
  39. (t) =>
  40. !t.disabled &&
  41. t.type === validParams.data.type &&
  42. t.engine.name === validParams.data.engine
  43. );
  44. }, [queryMatch]);
  45. return (
  46. <StyledTemplateComponent>
  47. {match(templateMatch)
  48. .with(
  49. { type: DATABASE_TYPE_RDS, engine: DATABASE_ENGINE_POSTGRES },
  50. (t) => <DatabaseFormRDSPostgres template={t} />
  51. )
  52. .with(
  53. { type: DATABASE_TYPE_AURORA, engine: DATABASE_ENGINE_POSTGRES },
  54. (t) => <DatabaseFormAuroraPostgres template={t} />
  55. )
  56. .with(
  57. { type: DATABASE_TYPE_ELASTICACHE, engine: DATABASE_ENGINE_REDIS },
  58. (t) => <DatabaseFormElasticacheRedis template={t} />
  59. )
  60. .otherwise(() => (
  61. <>
  62. <Back to="/databases" />
  63. <DashboardHeader
  64. image={database}
  65. title="Create a new database"
  66. capitalize={false}
  67. disableLineBreak
  68. />
  69. <Text size={15}>Production datastores</Text>
  70. <Spacer y={0.5} />
  71. <Text color="helper">
  72. Fully-managed production-ready datastores.
  73. </Text>
  74. <Spacer y={0.5} />
  75. <TemplateListWrapper>
  76. {SUPPORTED_DATABASE_TEMPLATES.map((template) => {
  77. const { name, icon, description, disabled, engine, type } =
  78. template;
  79. return (
  80. <TemplateBlock
  81. disabled={disabled}
  82. key={`${name}-${engine.name}`}
  83. onClick={() => {
  84. history.push(`/databases/new/${type}/${engine.name}`);
  85. }}
  86. >
  87. <TemplateHeader>
  88. <Icon src={icon} />
  89. <Spacer inline x={0.5} />
  90. <TemplateTitle>{name}</TemplateTitle>
  91. <Spacer inline x={0.5} />
  92. <Tag hoverable={false}>{engine.displayName}</Tag>
  93. </TemplateHeader>
  94. <Spacer y={0.5} />
  95. <TemplateDescription>{description}</TemplateDescription>
  96. <Spacer y={0.5} />
  97. </TemplateBlock>
  98. );
  99. })}
  100. </TemplateListWrapper>
  101. </>
  102. ))}
  103. </StyledTemplateComponent>
  104. );
  105. };
  106. export default withRouter(CreateDatabase);
  107. const Icon = styled.img`
  108. height: 18px;
  109. `;
  110. const StyledTemplateComponent = styled.div`
  111. width: 100%;
  112. height: 100%;
  113. `;
  114. const TemplateDescription = styled.div`
  115. display: flex;
  116. margin-bottom: 15px;
  117. color: #ffffff66;
  118. font-weight: default;
  119. padding: 0px 50px;
  120. line-height: 1.4;
  121. font-size: 14px;
  122. text-align: center;
  123. `;
  124. const TemplateHeader = styled.div`
  125. display: inline-flex;
  126. align-items: center;
  127. justify-content: center;
  128. margin-top: 50px;
  129. `;
  130. const TemplateTitle = styled.div`
  131. display: flex;
  132. width: 100%;
  133. justify-content: center;
  134. font-size: 22px;
  135. white-space: nowrap;
  136. `;
  137. const TemplateBlock = styled.div<{ disabled?: boolean }>`
  138. align-items: center;
  139. user-select: none;
  140. display: flex;
  141. filter: ${({ disabled }) => (disabled ? "grayscale(1)" : "")};
  142. cursor: ${(props) => (props.disabled ? "not-allowed" : "pointer")};
  143. font-size: 13px;
  144. flex-direction: column;
  145. align-item: center;
  146. height: 220px;
  147. color: #ffffff;
  148. position: relative;
  149. border-radius: 5px;
  150. background: ${(props) => props.theme.clickable.bg};
  151. border: 1px solid #494b4f;
  152. :hover {
  153. border: ${(props) => (props.disabled ? "" : "1px solid #7a7b80")};
  154. }
  155. animation: fadeIn 0.3s 0s;
  156. @keyframes fadeIn {
  157. from {
  158. opacity: 0;
  159. }
  160. to {
  161. opacity: 1;
  162. }
  163. }
  164. `;
  165. const TemplateListWrapper = styled.div`
  166. overflow: visible;
  167. margin-top: 15px;
  168. display: grid;
  169. grid-column-gap: 30px;
  170. grid-row-gap: 30px;
  171. grid-template-columns: repeat(2, 1fr);
  172. `;