|
@@ -8,14 +8,13 @@ import { PorterTemplate } from "shared/types";
|
|
|
import TabSelector from "components/TabSelector";
|
|
import TabSelector from "components/TabSelector";
|
|
|
import ExpandedTemplate from "./expanded-template/ExpandedTemplate";
|
|
import ExpandedTemplate from "./expanded-template/ExpandedTemplate";
|
|
|
import Loading from "components/Loading";
|
|
import Loading from "components/Loading";
|
|
|
|
|
+import LaunchFlow from "./launch-flow/LaunchFlow";
|
|
|
|
|
|
|
|
import hardcodedNames from "./hardcodedNameDict";
|
|
import hardcodedNames from "./hardcodedNameDict";
|
|
|
-import { Link } from "react-router-dom";
|
|
|
|
|
import semver from "semver";
|
|
import semver from "semver";
|
|
|
-import { version } from "html-webpack-plugin";
|
|
|
|
|
|
|
|
|
|
const tabOptions = [
|
|
const tabOptions = [
|
|
|
- { label: "New Application", value: "docker" },
|
|
|
|
|
|
|
+ { label: "New Application", value: "porter" },
|
|
|
{ label: "Community Add-ons", value: "community" },
|
|
{ label: "Community Add-ons", value: "community" },
|
|
|
];
|
|
];
|
|
|
|
|
|
|
@@ -28,16 +27,18 @@ type StateType = {
|
|
|
applicationTemplates: PorterTemplate[];
|
|
applicationTemplates: PorterTemplate[];
|
|
|
loading: boolean;
|
|
loading: boolean;
|
|
|
error: boolean;
|
|
error: boolean;
|
|
|
|
|
+ isOnLaunchFlow: boolean;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
export default class Templates extends Component<PropsType, StateType> {
|
|
export default class Templates extends Component<PropsType, StateType> {
|
|
|
state = {
|
|
state = {
|
|
|
currentTemplate: null as PorterTemplate | null,
|
|
currentTemplate: null as PorterTemplate | null,
|
|
|
- currentTab: "docker",
|
|
|
|
|
|
|
+ currentTab: "porter",
|
|
|
addonTemplates: [] as PorterTemplate[],
|
|
addonTemplates: [] as PorterTemplate[],
|
|
|
applicationTemplates: [] as PorterTemplate[],
|
|
applicationTemplates: [] as PorterTemplate[],
|
|
|
loading: true,
|
|
loading: true,
|
|
|
error: false,
|
|
error: false,
|
|
|
|
|
+ isOnLaunchFlow: false,
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
componentDidMount() {
|
|
@@ -128,8 +129,8 @@ export default class Templates extends Component<PropsType, StateType> {
|
|
|
);
|
|
);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- renderApplicationList = () => {
|
|
|
|
|
- let { loading, error, applicationTemplates } = this.state;
|
|
|
|
|
|
|
+ renderTemplateList = (templates: any) => {
|
|
|
|
|
+ let { loading, error } = this.state;
|
|
|
|
|
|
|
|
if (loading) {
|
|
if (loading) {
|
|
|
return (
|
|
return (
|
|
@@ -143,7 +144,7 @@ export default class Templates extends Component<PropsType, StateType> {
|
|
|
<i className="material-icons">error</i> Error retrieving templates.
|
|
<i className="material-icons">error</i> Error retrieving templates.
|
|
|
</Placeholder>
|
|
</Placeholder>
|
|
|
);
|
|
);
|
|
|
- } else if (applicationTemplates.length === 0) {
|
|
|
|
|
|
|
+ } else if (templates.length === 0) {
|
|
|
return (
|
|
return (
|
|
|
<Placeholder>
|
|
<Placeholder>
|
|
|
<i className="material-icons">category</i> No templates found.
|
|
<i className="material-icons">category</i> No templates found.
|
|
@@ -151,146 +152,108 @@ export default class Templates extends Component<PropsType, StateType> {
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return this.state.applicationTemplates.map(
|
|
|
|
|
- (template: PorterTemplate, i: number) => {
|
|
|
|
|
- let { name, icon, description } = template;
|
|
|
|
|
- if (hardcodedNames[name]) {
|
|
|
|
|
- name = hardcodedNames[name];
|
|
|
|
|
- }
|
|
|
|
|
- return (
|
|
|
|
|
- <TemplateBlock
|
|
|
|
|
- key={i}
|
|
|
|
|
- onClick={() => this.setState({ currentTemplate: template })}
|
|
|
|
|
- >
|
|
|
|
|
- {this.renderIcon(icon)}
|
|
|
|
|
- <TemplateTitle>{name}</TemplateTitle>
|
|
|
|
|
- <TemplateDescription>{description}</TemplateDescription>
|
|
|
|
|
- </TemplateBlock>
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
- );
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- renderAddonList = () => {
|
|
|
|
|
- let { loading, error, addonTemplates } = this.state;
|
|
|
|
|
-
|
|
|
|
|
- if (loading) {
|
|
|
|
|
- return (
|
|
|
|
|
- <LoadingWrapper>
|
|
|
|
|
- <Loading />
|
|
|
|
|
- </LoadingWrapper>
|
|
|
|
|
- );
|
|
|
|
|
- } else if (error) {
|
|
|
|
|
- return (
|
|
|
|
|
- <Placeholder>
|
|
|
|
|
- <i className="material-icons">error</i> Error retrieving templates.
|
|
|
|
|
- </Placeholder>
|
|
|
|
|
- );
|
|
|
|
|
- } else if (addonTemplates.length === 0) {
|
|
|
|
|
- return (
|
|
|
|
|
- <Placeholder>
|
|
|
|
|
- <i className="material-icons">category</i> No templates found.
|
|
|
|
|
- </Placeholder>
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return this.state.addonTemplates.map(
|
|
|
|
|
- (template: PorterTemplate, i: number) => {
|
|
|
|
|
- let { name, icon, description } = template;
|
|
|
|
|
- if (hardcodedNames[name]) {
|
|
|
|
|
- name = hardcodedNames[name];
|
|
|
|
|
- }
|
|
|
|
|
- return (
|
|
|
|
|
- <TemplateBlock
|
|
|
|
|
- key={i}
|
|
|
|
|
- onClick={() => this.setState({ currentTemplate: template })}
|
|
|
|
|
- >
|
|
|
|
|
- {this.renderIcon(icon)}
|
|
|
|
|
- <TemplateTitle>{name}</TemplateTitle>
|
|
|
|
|
- <TemplateDescription>{description}</TemplateDescription>
|
|
|
|
|
- </TemplateBlock>
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ return (
|
|
|
|
|
+ <TemplateList>
|
|
|
|
|
+ {templates.map(
|
|
|
|
|
+ (template: PorterTemplate, i: number) => {
|
|
|
|
|
+ let { name, icon, description } = template;
|
|
|
|
|
+ if (hardcodedNames[name]) {
|
|
|
|
|
+ name = hardcodedNames[name];
|
|
|
|
|
+ }
|
|
|
|
|
+ return (
|
|
|
|
|
+ <TemplateBlock
|
|
|
|
|
+ key={name}
|
|
|
|
|
+ onClick={() => this.setState({ currentTemplate: template })}
|
|
|
|
|
+ >
|
|
|
|
|
+ {this.renderIcon(icon)}
|
|
|
|
|
+ <TemplateTitle>{name}</TemplateTitle>
|
|
|
|
|
+ <TemplateDescription>{description}</TemplateDescription>
|
|
|
|
|
+ </TemplateBlock>
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ )}
|
|
|
|
|
+ </TemplateList>
|
|
|
);
|
|
);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- renderApplicationTemplates = () => {
|
|
|
|
|
- if (!this.context.currentCluster) {
|
|
|
|
|
- return (
|
|
|
|
|
- <>
|
|
|
|
|
- <Banner>
|
|
|
|
|
- <i className="material-icons">error_outline</i>
|
|
|
|
|
- <Link to="dashboard">Provision</Link> or
|
|
|
|
|
- <Link
|
|
|
|
|
- to="#"
|
|
|
|
|
- onClick={() =>
|
|
|
|
|
- this.context.setCurrentModal("ClusterInstructionsModal")
|
|
|
|
|
- }
|
|
|
|
|
- >
|
|
|
|
|
- connect
|
|
|
|
|
- </Link>
|
|
|
|
|
- to a cluster
|
|
|
|
|
- </Banner>
|
|
|
|
|
- </>
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ renderTabContents = () => {
|
|
|
if (this.state.currentTemplate) {
|
|
if (this.state.currentTemplate) {
|
|
|
return (
|
|
return (
|
|
|
<ExpandedTemplate
|
|
<ExpandedTemplate
|
|
|
|
|
+ showLaunchFlow={() => this.setState({ isOnLaunchFlow: true })}
|
|
|
currentTab={this.state.currentTab}
|
|
currentTab={this.state.currentTab}
|
|
|
currentTemplate={this.state.currentTemplate}
|
|
currentTemplate={this.state.currentTemplate}
|
|
|
setCurrentTemplate={(currentTemplate: PorterTemplate) => {
|
|
setCurrentTemplate={(currentTemplate: PorterTemplate) => {
|
|
|
this.setState({ currentTemplate });
|
|
this.setState({ currentTemplate });
|
|
|
}}
|
|
}}
|
|
|
- skipDescription={false}
|
|
|
|
|
/>
|
|
/>
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
- return <TemplateList>{this.renderApplicationList()}</TemplateList>;
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ if (this.state.currentTab === "porter") {
|
|
|
|
|
+ return this.renderTemplateList(this.state.applicationTemplates);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return this.renderTemplateList(this.state.addonTemplates);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- renderAddonTemplates = () => {
|
|
|
|
|
- if (this.state.currentTemplate) {
|
|
|
|
|
|
|
+ render() {
|
|
|
|
|
+ if (!this.state.isOnLaunchFlow || !this.state.currentTemplate) {
|
|
|
return (
|
|
return (
|
|
|
- <ExpandedTemplate
|
|
|
|
|
|
|
+ <TemplatesWrapper>
|
|
|
|
|
+ <TitleSection>
|
|
|
|
|
+ <Title>Launch</Title>
|
|
|
|
|
+ <a
|
|
|
|
|
+ href="https://docs.getporter.dev/docs/porter-templates"
|
|
|
|
|
+ target="_blank"
|
|
|
|
|
+ >
|
|
|
|
|
+ <i className="material-icons">help_outline</i>
|
|
|
|
|
+ </a>
|
|
|
|
|
+ </TitleSection>
|
|
|
|
|
+ {
|
|
|
|
|
+ this.context.currentCluster ? (
|
|
|
|
|
+ <>
|
|
|
|
|
+ <TabSelector
|
|
|
|
|
+ options={tabOptions}
|
|
|
|
|
+ currentTab={this.state.currentTab}
|
|
|
|
|
+ setCurrentTab={(value: string) =>
|
|
|
|
|
+ this.setState({
|
|
|
|
|
+ currentTab: value,
|
|
|
|
|
+ currentTemplate: null,
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ />
|
|
|
|
|
+ {this.renderTabContents()}
|
|
|
|
|
+ </>
|
|
|
|
|
+ ) : (
|
|
|
|
|
+ <>
|
|
|
|
|
+ <Banner>
|
|
|
|
|
+ <i className="material-icons">error_outline</i>
|
|
|
|
|
+ No cluster connected to this project.
|
|
|
|
|
+ </Banner>
|
|
|
|
|
+ <StyledStatusPlaceholder>
|
|
|
|
|
+ You need to connect a cluster to use Porter.
|
|
|
|
|
+ <Highlight
|
|
|
|
|
+ onClick={() => {
|
|
|
|
|
+ this.context.setCurrentModal("ClusterInstructionsModal", {});
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ + Connect an existing cluster
|
|
|
|
|
+ </Highlight>
|
|
|
|
|
+ </StyledStatusPlaceholder>
|
|
|
|
|
+ </>
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ </TemplatesWrapper>
|
|
|
|
|
+ );
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return (
|
|
|
|
|
+ <LaunchFlow
|
|
|
currentTab={this.state.currentTab}
|
|
currentTab={this.state.currentTab}
|
|
|
- currentTemplate={this.state.currentTemplate}
|
|
|
|
|
- setCurrentTemplate={(currentTemplate: PorterTemplate) => {
|
|
|
|
|
- this.setState({ currentTemplate });
|
|
|
|
|
- }}
|
|
|
|
|
|
|
+ currentTemplate={this.state.currentTemplate}
|
|
|
|
|
+ hideLaunchFlow={() => this.setState({ isOnLaunchFlow: false })}
|
|
|
/>
|
|
/>
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
- return <TemplateList>{this.renderAddonList()}</TemplateList>;
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- render() {
|
|
|
|
|
- return (
|
|
|
|
|
- <TemplatesWrapper>
|
|
|
|
|
- <TitleSection>
|
|
|
|
|
- <Title>Launch</Title>
|
|
|
|
|
- <a
|
|
|
|
|
- href="https://docs.getporter.dev/docs/porter-templates"
|
|
|
|
|
- target="_blank"
|
|
|
|
|
- >
|
|
|
|
|
- <i className="material-icons">help_outline</i>
|
|
|
|
|
- </a>
|
|
|
|
|
- </TitleSection>
|
|
|
|
|
- <TabSelector
|
|
|
|
|
- options={tabOptions}
|
|
|
|
|
- currentTab={this.state.currentTab}
|
|
|
|
|
- setCurrentTab={(value: string) =>
|
|
|
|
|
- this.setState({
|
|
|
|
|
- currentTab: value,
|
|
|
|
|
- currentTemplate: null,
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
- />
|
|
|
|
|
- {this.state.currentTab === "docker"
|
|
|
|
|
- ? this.renderApplicationTemplates()
|
|
|
|
|
- : this.renderAddonTemplates()}
|
|
|
|
|
- </TemplatesWrapper>
|
|
|
|
|
- );
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -327,6 +290,31 @@ const Banner = styled.div`
|
|
|
}
|
|
}
|
|
|
`;
|
|
`;
|
|
|
|
|
|
|
|
|
|
+const Highlight = styled.div`
|
|
|
|
|
+ color: #8590ff;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ margin-left: 5px;
|
|
|
|
|
+ margin-right: 10px;
|
|
|
|
|
+`;
|
|
|
|
|
+
|
|
|
|
|
+const StyledStatusPlaceholder = styled.div`
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: calc(100vh - 365px);
|
|
|
|
|
+ margin-top: 20px;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ color: #aaaabb;
|
|
|
|
|
+ border-radius: 5px;
|
|
|
|
|
+ padding-bottom: 20px;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ background: #ffffff09;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ font-family: "Work Sans", sans-serif;
|
|
|
|
|
+ user-select: text;
|
|
|
|
|
+`;
|
|
|
|
|
+
|
|
|
const LoadingWrapper = styled.div`
|
|
const LoadingWrapper = styled.div`
|
|
|
padding-top: 300px;
|
|
padding-top: 300px;
|
|
|
`;
|
|
`;
|