|
|
@@ -1,197 +1,89 @@
|
|
|
+import React, { useState } from "react";
|
|
|
import _ from "lodash";
|
|
|
-import React, { useContext, useMemo, useState } from "react";
|
|
|
import styled from "styled-components";
|
|
|
-import DashboardHeader from "../cluster-dashboard/DashboardHeader";
|
|
|
-
|
|
|
-import awsRDS from "assets/amazon-rds.png";
|
|
|
-import awsElastiCache from "assets/aws-elasticache.png";
|
|
|
-import database from "assets/database.svg";
|
|
|
-import notFound from "assets/not-found.png";
|
|
|
-
|
|
|
-import { Context } from "shared/Context";
|
|
|
-import { search } from "shared/search";
|
|
|
-import { AddonCard } from "shared/types";
|
|
|
+import { match } from "ts-pattern";
|
|
|
|
|
|
import Back from "components/porter/Back";
|
|
|
-import Container from "components/porter/Container";
|
|
|
-import Fieldset from "components/porter/Fieldset";
|
|
|
import Spacer from "components/porter/Spacer";
|
|
|
+import Tag from "components/porter/Tag";
|
|
|
import Text from "components/porter/Text";
|
|
|
-import AuroraPostgresForm from "./forms/AuroraPostgresForm";
|
|
|
-import ElasticacheRedisForm from "./forms/ElasticacheRedisForm";
|
|
|
-import RDSForm from "./forms/RDSForm";
|
|
|
|
|
|
-type Props = {
|
|
|
-};
|
|
|
+import database from "assets/database.svg";
|
|
|
+
|
|
|
+import DashboardHeader from "../cluster-dashboard/DashboardHeader";
|
|
|
+import { SUPPORTED_DATABASE_TEMPLATES } from "./constants";
|
|
|
+import DatabaseForm from "./forms/DatabaseForm";
|
|
|
+import { type DatabaseTemplate } from "./types";
|
|
|
|
|
|
-const CreateDatabase: React.FC<Props> = ({
|
|
|
-}) => {
|
|
|
- const { capabilities, currentProject, currentCluster, user } = useContext(Context);
|
|
|
- const [isLoading, setIsLoading] = useState<boolean>(true);
|
|
|
- const [searchValue, setSearchValue] = useState("");
|
|
|
- const [currentTemplate, setCurrentTemplate] = useState<any>(null);
|
|
|
- const [databaseTemplates, setDatabaseTemplates] = useState<AddonCard[]>([
|
|
|
- {
|
|
|
- id: "rds-postgresql",
|
|
|
- icon: awsRDS,
|
|
|
- name: "RDS PostgreSQL",
|
|
|
- description: "Amazon Relational Database Service (RDS) is a web service that makes it easier to set up, operate, and scale a relational database in the cloud.",
|
|
|
- },
|
|
|
- {
|
|
|
- id: "rds-postgresql-aurora",
|
|
|
- icon: awsRDS,
|
|
|
- name: "Aurora PostgreSQL",
|
|
|
- description: "Amazon Aurora PostgreSQL is a fully managed, PostgreSQL–compatible, and ACID–compliant relational database engine that combines the speed, reliability, and manageability of Amazon Aurora with the simplicity and cost-effectiveness of open-source databases.",
|
|
|
- },
|
|
|
- {
|
|
|
- id: "elasticache-redis",
|
|
|
- icon: awsElastiCache,
|
|
|
- name: "ElastiCache Redis",
|
|
|
- description: "A fast nosql datastore used for caching, real-time interactions, and more.",
|
|
|
- },
|
|
|
- {
|
|
|
- id: "elasticache-memcached",
|
|
|
- icon: awsElastiCache,
|
|
|
- name: "ElastiCache Memcached",
|
|
|
- description: "Contact support@porter.run",
|
|
|
- disabled: true,
|
|
|
- },
|
|
|
- ]);
|
|
|
-
|
|
|
- const allFilteredTemplates = useMemo(() => {
|
|
|
- const filteredBySearch = search(
|
|
|
- databaseTemplates ?? [],
|
|
|
- searchValue,
|
|
|
- {
|
|
|
- keys: ["name"],
|
|
|
- isCaseSensitive: false,
|
|
|
- }
|
|
|
- );
|
|
|
- return _.sortBy(filteredBySearch);
|
|
|
- }, [databaseTemplates, searchValue]);
|
|
|
+const CreateDatabase: React.FC = () => {
|
|
|
+ const [selectedTemplate, setSelectedTemplate] = useState<
|
|
|
+ DatabaseTemplate | undefined
|
|
|
+ >(undefined);
|
|
|
|
|
|
return (
|
|
|
<StyledTemplateComponent>
|
|
|
- {
|
|
|
- (currentTemplate) ? (
|
|
|
- <>
|
|
|
- {currentTemplate.id === "rds-postgresql" && (
|
|
|
- <RDSForm
|
|
|
- currentTemplate={currentTemplate}
|
|
|
- goBack={() => setCurrentTemplate(null)}
|
|
|
- repoURL={capabilities?.default_addon_helm_repo_url}
|
|
|
- />
|
|
|
- )}
|
|
|
- {currentTemplate.id === "rds-postgresql-aurora" && (
|
|
|
- <AuroraPostgresForm
|
|
|
- currentTemplate={currentTemplate}
|
|
|
- goBack={() => setCurrentTemplate(null)}
|
|
|
- repoURL={capabilities?.default_addon_helm_repo_url}
|
|
|
+ {match(selectedTemplate)
|
|
|
+ .with(undefined, () => {
|
|
|
+ return (
|
|
|
+ <>
|
|
|
+ <Back to="/databases" />
|
|
|
+ <DashboardHeader
|
|
|
+ image={database}
|
|
|
+ title="Create a new database"
|
|
|
+ capitalize={false}
|
|
|
+ disableLineBreak
|
|
|
/>
|
|
|
- )}
|
|
|
- {currentTemplate.id === "elasticache-redis" && (
|
|
|
- <ElasticacheRedisForm
|
|
|
- currentTemplate={currentTemplate}
|
|
|
- goBack={() => setCurrentTemplate(null)}
|
|
|
- repoURL={capabilities?.default_addon_helm_repo_url}
|
|
|
- />
|
|
|
- )}
|
|
|
- </>
|
|
|
- ) : (
|
|
|
- <>
|
|
|
- <Back to="/databases" />
|
|
|
- <DashboardHeader
|
|
|
- image={database}
|
|
|
- title="Create a new database"
|
|
|
- capitalize={false}
|
|
|
- disableLineBreak
|
|
|
- />
|
|
|
- {/*
|
|
|
- <SearchBar
|
|
|
- value={searchValue}
|
|
|
- setValue={setSearchValue}
|
|
|
- placeholder="Search available databases . . ."
|
|
|
- width="100%"
|
|
|
- />
|
|
|
- <Spacer y={1} />
|
|
|
- */}
|
|
|
-
|
|
|
- {allFilteredTemplates.length === 0 && (
|
|
|
- <Fieldset>
|
|
|
- <Container row>
|
|
|
- <PlaceholderIcon src={notFound} />
|
|
|
- <Text color="helper">No matching add-ons were found.</Text>
|
|
|
- </Container>
|
|
|
- </Fieldset>
|
|
|
- )}
|
|
|
- <DarkMatter />
|
|
|
-
|
|
|
- {databaseTemplates?.length > 0 &&
|
|
|
- <>
|
|
|
- <Spacer y={1.5} />
|
|
|
- <Text size={15}>Production datastores</Text>
|
|
|
- <Spacer y={.5} />
|
|
|
- <Text color="helper">Fully-managed production-ready datastores.</Text>
|
|
|
- <Spacer y={.5} />
|
|
|
- <TemplateListWrapper>
|
|
|
- {databaseTemplates.map((template) => {
|
|
|
- let { id, name, icon, description, tags, disabled } = template;
|
|
|
- return (
|
|
|
- <TemplateBlock
|
|
|
- disabled={disabled}
|
|
|
- key={id}
|
|
|
- onClick={() => {
|
|
|
- !disabled && setCurrentTemplate(template);
|
|
|
- }}
|
|
|
- >
|
|
|
+ <Text size={15}>Production datastores</Text>
|
|
|
+ <Spacer y={0.5} />
|
|
|
+ <Text color="helper">
|
|
|
+ Fully-managed production-ready datastores.
|
|
|
+ </Text>
|
|
|
+ <Spacer y={0.5} />
|
|
|
+ <TemplateListWrapper>
|
|
|
+ {SUPPORTED_DATABASE_TEMPLATES.map((template) => {
|
|
|
+ const { name, icon, description, disabled, engine } =
|
|
|
+ template;
|
|
|
+ return (
|
|
|
+ <TemplateBlock
|
|
|
+ disabled={disabled}
|
|
|
+ key={name}
|
|
|
+ onClick={() => {
|
|
|
+ !disabled && setSelectedTemplate(template);
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <TemplateHeader>
|
|
|
<Icon src={icon} />
|
|
|
+ <Spacer inline x={0.5} />
|
|
|
<TemplateTitle>{name}</TemplateTitle>
|
|
|
- <TemplateDescription>{description}</TemplateDescription>
|
|
|
- <Spacer y={0.5} />
|
|
|
- </TemplateBlock>
|
|
|
- );
|
|
|
- })}
|
|
|
- </TemplateListWrapper>
|
|
|
- </>
|
|
|
- }
|
|
|
- </>
|
|
|
- )
|
|
|
- }
|
|
|
- </StyledTemplateComponent >
|
|
|
+ <Spacer inline x={0.5} />
|
|
|
+ <Tag hoverable={false}>{engine.displayName}</Tag>
|
|
|
+ </TemplateHeader>
|
|
|
+ <Spacer y={0.5} />
|
|
|
+ <TemplateDescription>{description}</TemplateDescription>
|
|
|
+ <Spacer y={0.5} />
|
|
|
+ </TemplateBlock>
|
|
|
+ );
|
|
|
+ })}
|
|
|
+ </TemplateListWrapper>
|
|
|
+ </>
|
|
|
+ );
|
|
|
+ })
|
|
|
+ .otherwise((tp) => (
|
|
|
+ <DatabaseForm
|
|
|
+ template={tp}
|
|
|
+ onFormExit={() => {
|
|
|
+ setSelectedTemplate(undefined);
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ ))}
|
|
|
+ </StyledTemplateComponent>
|
|
|
);
|
|
|
};
|
|
|
|
|
|
export default CreateDatabase;
|
|
|
|
|
|
const Icon = styled.img`
|
|
|
- height: 25px;
|
|
|
- margin-top: 30px;
|
|
|
- margin-bottom: 5px;
|
|
|
-`;
|
|
|
-
|
|
|
-const PlaceholderIcon = styled.img`
|
|
|
- height: 13px;
|
|
|
- margin-right: 12px;
|
|
|
- opacity: 0.65;
|
|
|
-`;
|
|
|
-
|
|
|
-const DarkMatter = styled.div`
|
|
|
- width: 100%;
|
|
|
- margin-top: -35px;
|
|
|
-`;
|
|
|
-
|
|
|
-const I = styled.i`
|
|
|
- font-size: 16px;
|
|
|
- padding: 4px;
|
|
|
- cursor: pointer;
|
|
|
- border-radius: 50%;
|
|
|
- margin-right: 15px;
|
|
|
- background: ${props => props.theme.fg};
|
|
|
- color: ${props => props.theme.text.primary};
|
|
|
- border: 1px solid ${props => props.theme.border};
|
|
|
- :hover {
|
|
|
- filter: brightness(150%);
|
|
|
- }
|
|
|
+ height: 18px;
|
|
|
`;
|
|
|
|
|
|
const StyledTemplateComponent = styled.div`
|
|
|
@@ -200,26 +92,29 @@ const StyledTemplateComponent = styled.div`
|
|
|
`;
|
|
|
|
|
|
const TemplateDescription = styled.div`
|
|
|
+ display: flex;
|
|
|
margin-bottom: 15px;
|
|
|
color: #ffffff66;
|
|
|
- text-align: center;
|
|
|
font-weight: default;
|
|
|
- padding: 0px 25px;
|
|
|
+ padding: 0px 50px;
|
|
|
line-height: 1.4;
|
|
|
- font-size: 12px;
|
|
|
- display: -webkit-box;
|
|
|
- overflow: hidden;
|
|
|
- -webkit-line-clamp: 2;
|
|
|
- -webkit-box-orient: vertical;
|
|
|
+ font-size: 14px;
|
|
|
+ text-align: center;
|
|
|
+`;
|
|
|
+
|
|
|
+const TemplateHeader = styled.div`
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ margin-top: 50px;
|
|
|
`;
|
|
|
|
|
|
const TemplateTitle = styled.div`
|
|
|
- width: 80%;
|
|
|
- text-align: center;
|
|
|
- font-size: 14px;
|
|
|
+ display: flex;
|
|
|
+ width: 100%;
|
|
|
+ justify-content: center;
|
|
|
+ font-size: 22px;
|
|
|
white-space: nowrap;
|
|
|
- overflow: hidden;
|
|
|
- text-overflow: ellipsis;
|
|
|
`;
|
|
|
|
|
|
const TemplateBlock = styled.div<{ disabled?: boolean }>`
|
|
|
@@ -231,12 +126,11 @@ const TemplateBlock = styled.div<{ disabled?: boolean }>`
|
|
|
font-size: 13px;
|
|
|
flex-direction: column;
|
|
|
align-item: center;
|
|
|
- justify-content: space-between;
|
|
|
- height: 180px;
|
|
|
+ height: 220px;
|
|
|
color: #ffffff;
|
|
|
position: relative;
|
|
|
border-radius: 5px;
|
|
|
- background: ${props => props.theme.clickable.bg};
|
|
|
+ background: ${(props) => props.theme.clickable.bg};
|
|
|
border: 1px solid #494b4f;
|
|
|
:hover {
|
|
|
border: ${(props) => (props.disabled ? "" : "1px solid #7a7b80")};
|
|
|
@@ -256,9 +150,8 @@ const TemplateBlock = styled.div<{ disabled?: boolean }>`
|
|
|
const TemplateListWrapper = styled.div`
|
|
|
overflow: visible;
|
|
|
margin-top: 15px;
|
|
|
- padding-bottom: 50px;
|
|
|
display: grid;
|
|
|
grid-column-gap: 30px;
|
|
|
grid-row-gap: 30px;
|
|
|
- grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
|
-`;
|
|
|
+ grid-template-columns: repeat(2, 1fr);
|
|
|
+`;
|