diff --git a/src/features/linodes/LinodesCreate/CALinodeCreate.tsx b/src/features/linodes/LinodesCreate/CALinodeCreate.tsx
index 1f05f8d80bd..d243849e532 100644
--- a/src/features/linodes/LinodesCreate/CALinodeCreate.tsx
+++ b/src/features/linodes/LinodesCreate/CALinodeCreate.tsx
@@ -93,11 +93,21 @@ export class LinodeCreate extends React.PureComponent<
linodesData,
linodesError,
linodesLoading,
+ handleSelectUDFs,
+ selectedUDFs,
+ updateStackScript,
+ availableStackScriptImages,
+ availableUserDefinedFields,
+ selectedStackScriptID,
+ selectedDiskSize,
+ selectedStackScriptUsername,
+ selectedStackScriptLabel,
+ selectedLinodeID,
...rest
} = this.props;
return (
@@ -136,14 +146,45 @@ export class LinodeCreate extends React.PureComponent<
myImagesTabs = (): Tab[] => [
{
- title: 'Backups and My Images',
+ title: 'Images',
+ type: 'fromImage',
+ render: () => {
+ const {
+ history,
+ linodesData,
+ linodesError,
+ linodesLoading,
+ handleSelectUDFs,
+ selectedUDFs,
+ updateStackScript,
+ availableStackScriptImages,
+ availableUserDefinedFields,
+ selectedStackScriptID,
+ selectedDiskSize,
+ selectedStackScriptUsername,
+ selectedStackScriptLabel,
+ selectedLinodeID,
+ ...rest
+ } = this.props;
+
+ return (
+
+ );
+ }
+ },
+ {
+ title: 'Backups',
type: 'fromBackup',
render: () => {
return ;
}
},
{
- title: 'Clone from Existing Linode',
+ title: 'Clone Linode',
type: 'fromLinode',
render: () => {
/**
diff --git a/src/features/linodes/LinodesCreate/Panel.tsx b/src/features/linodes/LinodesCreate/Panel.tsx
new file mode 100644
index 00000000000..7c85cece145
--- /dev/null
+++ b/src/features/linodes/LinodesCreate/Panel.tsx
@@ -0,0 +1,48 @@
+import * as React from 'react';
+
+import {
+ StyleRulesCallback,
+ withStyles,
+ WithStyles
+} from 'src/components/core/styles';
+
+import Paper from 'src/components/core/Paper';
+import Typography from 'src/components/core/Typography';
+import Notice from 'src/components/Notice';
+
+type ClassNames = 'root' | 'flatImagePanel';
+
+const styles: StyleRulesCallback = theme => ({
+ flatImagePanel: {
+ padding: theme.spacing.unit * 3
+ },
+ root: {}
+});
+
+interface Props {
+ children: React.ReactElement;
+ error?: string;
+ title?: string;
+}
+
+type CombinedProps = Props & WithStyles;
+
+const Panel: React.StatelessComponent = (props) => {
+ const { classes, children, error, title } = props;
+ return (
+
+ {error && }
+
+ {title || 'Select an Image'}
+
+ {children}
+
+ )
+}
+
+const styled = withStyles(styles);
+
+export default styled(Panel);
\ No newline at end of file
diff --git a/src/features/linodes/LinodesCreate/PrivateImages.tsx b/src/features/linodes/LinodesCreate/PrivateImages.tsx
new file mode 100644
index 00000000000..1ba1de7edf4
--- /dev/null
+++ b/src/features/linodes/LinodesCreate/PrivateImages.tsx
@@ -0,0 +1,51 @@
+import * as React from 'react';
+
+import {
+ StyleRulesCallback,
+ withStyles,
+ WithStyles
+} from 'src/components/core/styles';
+import Grid from 'src/components/Grid';
+import SelectionCard from 'src/components/SelectionCard';
+
+type ClassNames = 'root' | 'flatImagePanelSelections';
+
+const styles: StyleRulesCallback = theme => ({
+ flatImagePanelSelections: {
+ marginTop: theme.spacing.unit * 2,
+ padding: `${theme.spacing.unit}px 0`
+ },
+ root: {}
+});
+interface Props {
+ images: Linode.Image[];
+ disabled?: boolean;
+ selectedImageID?: string;
+ handleSelection: (id: string) => void;
+}
+
+type CombinedProps = Props & WithStyles;
+
+const PrivateImages: React.StatelessComponent = (props) => {
+ const { classes, disabled, handleSelection, images, selectedImageID } = props;
+ return (
+
+ {images &&
+ images.map((image: Linode.Image, idx: number) => (
+ handleSelection(image.id)}
+ renderIcon={() => }
+ heading={image.label as string}
+ subheadings={[image.description as string]}
+ disabled={disabled}
+ />
+ ))}
+
+ )
+}
+
+const styled = withStyles(styles);
+
+export default styled(PrivateImages);
\ No newline at end of file
diff --git a/src/features/linodes/LinodesCreate/PublicImages.tsx b/src/features/linodes/LinodesCreate/PublicImages.tsx
new file mode 100644
index 00000000000..a6d4335b22c
--- /dev/null
+++ b/src/features/linodes/LinodesCreate/PublicImages.tsx
@@ -0,0 +1,89 @@
+import * as React from 'react';
+
+import {
+ StyleRulesCallback,
+ withStyles,
+ WithStyles
+} from 'src/components/core/styles';
+import Grid from 'src/components/Grid';
+import SelectionCard from 'src/components/SelectionCard';
+import ShowMoreExpansion from 'src/components/ShowMoreExpansion';
+
+type ClassNames = 'root' | 'flatImagePanelSelections';
+
+const styles: StyleRulesCallback = theme => ({
+ flatImagePanelSelections: {
+ marginTop: theme.spacing.unit * 2,
+ padding: `${theme.spacing.unit}px 0`
+ },
+ root: {}
+});
+interface Props {
+ images: Linode.Image[];
+ oldImages: Linode.Image[];
+ selectedImageID?: string;
+ disabled?: boolean;
+ handleSelection: (id: string) => void;
+}
+
+type CombinedProps = Props & WithStyles;
+
+const distroIcons = {
+ Arch: 'archlinux',
+ CentOS: 'centos',
+ CoreOS: 'coreos',
+ Debian: 'debian',
+ Fedora: 'fedora',
+ Gentoo: 'gentoo',
+ openSUSE: 'opensuse',
+ Slackware: 'slackware',
+ Ubuntu: 'ubuntu'
+};
+
+const PublicImages: React.StatelessComponent = props => {
+ const {
+ classes,
+ disabled,
+ images,
+ handleSelection,
+ oldImages,
+ selectedImageID
+ } = props;
+ const renderImages = (images: Linode.Image[]) =>
+ images.length &&
+ images.map((image: Linode.Image, idx: number) => (
+ handleSelection(image.id)}
+ renderIcon={() => {
+ return (
+
+ );
+ }}
+ heading={image.vendor as string}
+ subheadings={[image.label]}
+ data-qa-selection-card
+ disabled={disabled}
+ />
+ ));
+
+ return (
+ <>
+
+ {renderImages(images)}
+
+ {oldImages.length > 0 && (
+
+
+ {renderImages(oldImages)}
+
+
+ )}
+ >
+ );
+};
+
+const styled = withStyles(styles);
+
+export default styled(PublicImages);
diff --git a/src/features/linodes/LinodesCreate/SelectImagePanel.tsx b/src/features/linodes/LinodesCreate/SelectImagePanel.tsx
index dd09c768d88..7f1acb84cc4 100644
--- a/src/features/linodes/LinodesCreate/SelectImagePanel.tsx
+++ b/src/features/linodes/LinodesCreate/SelectImagePanel.tsx
@@ -15,44 +15,12 @@ import {
values
} from 'ramda';
import * as React from 'react';
-import Paper from 'src/components/core/Paper';
-import {
- StyleRulesCallback,
- withStyles,
- WithStyles
-} from 'src/components/core/styles';
-import Typography from 'src/components/core/Typography';
-import Grid from 'src/components/Grid';
-import Notice from 'src/components/Notice';
import RenderGuard from 'src/components/RenderGuard';
-import SelectionCard from 'src/components/SelectionCard';
-import ShowMoreExpansion from 'src/components/ShowMoreExpansion';
import TabbedPanel from 'src/components/TabbedPanel';
-type ClassNames = 'root' | 'flatImagePanel' | 'flatImagePanelSelections';
-
-const styles: StyleRulesCallback = theme => ({
- flatImagePanel: {
- padding: theme.spacing.unit * 3
- },
- flatImagePanelSelections: {
- marginTop: theme.spacing.unit * 2,
- padding: `${theme.spacing.unit}px 0`
- },
- root: {}
-});
-
-const distroIcons = {
- Arch: 'archlinux',
- CentOS: 'centos',
- CoreOS: 'coreos',
- Debian: 'debian',
- Fedora: 'fedora',
- Gentoo: 'gentoo',
- openSUSE: 'opensuse',
- Slackware: 'slackware',
- Ubuntu: 'ubuntu'
-};
+import Panel from './Panel';
+import PrivateImages from './PrivateImages';
+import PublicImages from './PublicImages';
interface Props {
images: Linode.Image[];
@@ -60,7 +28,7 @@ interface Props {
error?: string;
selectedImageID?: string;
handleSelection: (id: string) => void;
- hideMyImages?: boolean;
+ variant?: 'public' | 'private' | 'all';
initTab?: number;
disabled?: boolean;
}
@@ -105,130 +73,57 @@ export const getMyImages = compose(
filter(propSatisfies(startsWith('private'), 'id'))
);
-type CombinedProps = Props & WithStyles;
+type CombinedProps = Props;
const CreateFromImage: React.StatelessComponent = props => {
- const { images, error, handleSelection, disabled } = props;
+ const { images, error, handleSelection, disabled, title, variant, selectedImageID } = props;
const publicImages = getPublicImages(images);
const olderPublicImages = getOlderPublicImages(images);
const myImages = getMyImages(images);
- const renderPublicImages = () =>
- publicImages.length &&
- publicImages.map((image: Linode.Image, idx: number) => (
- handleSelection(image.id)}
- renderIcon={() => {
- return (
-
- );
- }}
- heading={image.vendor as string}
- subheadings={[image.label]}
- data-qa-selection-card
- disabled={disabled}
- />
- ));
+ const Public = (
+
+
+
+ )
- const renderOlderPublicImages = () =>
- olderPublicImages.length &&
- olderPublicImages.map((image: Linode.Image, idx: number) => (
- handleSelection(image.id)}
- renderIcon={() => {
- return (
-
- );
- }}
- heading={image.vendor as string}
- subheadings={[image.label]}
- disabled={disabled}
- />
- ));
+ const Private = (
+
+
+
+ )
const tabs = [
{
title: 'Public Images',
render: () => (
-
-
- {renderPublicImages()}
-
-
-
- {renderOlderPublicImages()}
-
-
-
+ Public
)
},
{
title: 'My Images',
render: () => (
-
- {myImages &&
- myImages.map((image: Linode.Image, idx: number) => (
- handleSelection(image.id)}
- renderIcon={() => }
- heading={image.label as string}
- subheadings={[image.description as string]}
- disabled={disabled}
- />
- ))}
-
+ Private
)
}
];
- const renderTabs = () => {
- const { hideMyImages } = props;
- if (hideMyImages) {
- return tabs;
- }
- return tabs;
- };
-
- return (
-
- {props.hideMyImages !== true ? ( // if we have no olderPublicImage, hide the dropdown
+ switch (variant) {
+ case 'private':
+ return Private;
+ case 'public':
+ return Public;
+ case 'all':
+ default:
+ return (
- ) : (
-
- {error && }
-
- {props.title || 'Select an Image'}
-
-
- {renderPublicImages()}
-
- {olderPublicImages.length > 0 && (
-
-
- {renderOlderPublicImages()}
-
-
- )}
-
- )}
-
- );
+ )
+ }
};
-const styled = withStyles(styles);
-
-export default styled(RenderGuard(CreateFromImage));
+export default (RenderGuard(CreateFromImage));
diff --git a/src/features/linodes/LinodesCreate/TabbedContent/FromImageContent.tsx b/src/features/linodes/LinodesCreate/TabbedContent/FromImageContent.tsx
index a06b1ce0180..cb328d13e53 100644
--- a/src/features/linodes/LinodesCreate/TabbedContent/FromImageContent.tsx
+++ b/src/features/linodes/LinodesCreate/TabbedContent/FromImageContent.tsx
@@ -1,5 +1,6 @@
import { pathOr } from 'ramda';
import * as React from 'react';
+import { Link } from 'react-router-dom';
import { Sticky, StickyProps } from 'react-sticky';
import { compose } from 'recompose';
import AccessPanel from 'src/components/AccessPanel';
@@ -13,6 +14,7 @@ import CreateLinodeDisabled from 'src/components/CreateLinodeDisabled';
import Grid from 'src/components/Grid';
import LabelAndTagsPanel from 'src/components/LabelAndTagsPanel';
import Notice from 'src/components/Notice';
+import Placeholder from 'src/components/Placeholder';
import SelectRegionPanel from 'src/components/SelectRegionPanel';
import getAPIErrorsFor from 'src/utilities/getAPIErrorFor';
import AddonsPanel from '../AddonsPanel';
@@ -41,7 +43,7 @@ interface Notice {
interface Props extends BaseFormStateAndHandlers {
notice?: Notice;
- publicOnly?: boolean;
+ variant?: 'public' | 'private' | 'all';
imagePanelTitle?: string;
}
@@ -97,17 +99,35 @@ export class FromImageContent extends React.PureComponent {
regionDisplayInfo,
typeDisplayInfo,
backupsMonthlyPrice,
- publicOnly,
userSSHKeys,
userCannotCreateLinode,
errors,
- imagePanelTitle
+ imagePanelTitle,
+ variant
} = this.props;
const hasErrorFor = getAPIErrorsFor(errorResources, errors);
const generalError = hasErrorFor('none');
const hasBackups = this.props.backupsEnabled || accountBackupsEnabled;
+ const privateImages = images.filter(image => !image.is_public);
+
+ if (variant === 'private' && privateImages.length === 0) {
+ return (
+
+
+ You don't have any private Images. Visit the{' '}
+ Images section to create an Image from
+ one of your Linode's disks.
+
+ }
+ />
+
+ );
+ }
return (
@@ -122,7 +142,7 @@ export class FromImageContent extends React.PureComponent {
{generalError && }
{
updateFor={[selectedImageID, compatibleImages, errors]}
selectedImageID={selectedImageID}
error={hasErrorFor('image')}
- hideMyImages={true}
+ variant="public"
/>
) : (
diff --git a/src/store/linodeCreate/linodeCreate.reducer.ts b/src/store/linodeCreate/linodeCreate.reducer.ts
index ebc2ab45e8c..a7bb1990a55 100644
--- a/src/store/linodeCreate/linodeCreate.reducer.ts
+++ b/src/store/linodeCreate/linodeCreate.reducer.ts
@@ -54,8 +54,6 @@ const reducer: Reducer = reducerWithInitialState(
).caseWithAction(handleChangeCreateType, (state, action) => {
const { payload } = action;
- console.log(payload);
-
return {
...state,
type: payload