Skip to content

CONSOLE-4573: Improve getting started message #15044

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,10 @@ export const ConsoleEmptyState: React.FC<ConsoleEmptyStateProps> = ({
ConsoleEmptyState.displayName = 'ConsoleEmptyState';

type ConsoleEmptyStateProps = Partial<EmptyStateProps> & {
className?: string;
variant?: EmptyStateProps['variant'];
'data-test'?: string;
Icon?: React.ComponentType;
Icon?: EmptyStateProps['icon'];
primaryActions?: React.ReactElement[];
secondaryActions?: React.ReactElement[];
title?: string | React.ReactElement;
variant?: EmptyStateVariant;
title?: EmptyStateProps['title'];
};
14 changes: 1 addition & 13 deletions frontend/public/components/namespace.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -764,18 +764,6 @@ export const ProjectsTable = (props) => {
const headerWithMetrics = () => projectTableHeader({ showMetrics: true, showActions: true });
const headerNoMetrics = () => projectTableHeader({ showMetrics: false, showActions: true });

const ProjectNotFoundMessage = () => {
const { t } = useTranslation();
const canCreateNs = useFlag(FLAGS.CAN_CREATE_NS);
const canCreateProject = useFlag(FLAGS.CAN_CREATE_PROJECT);
const canCreate = canCreateNs || canCreateProject;
return (
<ConsoleEmptyState title={t('public~Welcome to OpenShift')}>
<OpenShiftGettingStarted canCreate={canCreate} />
</ConsoleEmptyState>
);
};

const ProjectEmptyMessage = () => {
const { t } = useTranslation();
return (
Expand Down Expand Up @@ -835,7 +823,7 @@ export const ProjectList = ({ data, ...tableProps }) => {
data={data}
Header={showMetrics ? headerWithMetrics : headerNoMetrics}
Row={ProjectTableRow}
NoDataEmptyMsg={ProjectNotFoundMessage}
NoDataEmptyMsg={OpenShiftGettingStarted}
EmptyMsg={ProjectEmptyMessage}
customData={customData}
virtualize
Expand Down
207 changes: 104 additions & 103 deletions frontend/public/components/start-guide.tsx
Original file line number Diff line number Diff line change
@@ -1,130 +1,131 @@
import * as _ from 'lodash-es';
import * as React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom-v5-compat';
import { Button, Hint, HintTitle, HintBody } from '@patternfly/react-core';
import { useTranslation, Trans } from 'react-i18next';
import { useSelector } from 'react-redux';
import { Button, ButtonVariant, Divider, EmptyStateVariant } from '@patternfly/react-core';
import { useTranslation } from 'react-i18next';

import { FLAGS } from '@console/shared/src/constants';
import { useActiveNamespace } from '@console/shared/src/hooks/useActiveNamespace';
import { useActivePerspective } from '@console/dynamic-plugin-sdk';
import { createProjectMessageStateToProps } from '../reducers/ui';
import { Disabled, ExternalLink, openshiftHelpBase, LinkifyExternal } from './utils';
import { connectToFlags } from '../reducers/connectToFlags';
import { Disabled, openshiftHelpBase, LinkifyExternal, ConsoleEmptyState } from './utils';
import { ProjectModel } from '../models';
import { K8sResourceKind } from '../module/k8s/types';
import { useCreateNamespaceOrProjectModal } from '@console/shared/src/hooks/useCreateNamespaceOrProjectModal';
import { RootState } from '../redux';
import { useFlag } from '@console/shared/src';
import { ClusterIcon, ExternalLinkAltIcon } from '@patternfly/react-icons';

export const OpenShiftGettingStarted = connect(createProjectMessageStateToProps)(
({ canCreate = true, createProjectMessage }: OpenShiftGettingStartedProps) => {
const { t } = useTranslation();
const [, setActiveNamespace] = useActiveNamespace();
const [perspective] = useActivePerspective();
const createNamespaceOrProjectModal = useCreateNamespaceOrProjectModal();
export const OpenShiftGettingStarted: React.FCC<OpenShiftGettingStartedProps> = () => {
const { t } = useTranslation();
const [, setActiveNamespace] = useActiveNamespace();
const [perspective] = useActivePerspective();
const canCreateNamespace = useFlag(FLAGS.CAN_CREATE_NS);
const canCreateProject = useFlag(FLAGS.CAN_CREATE_PROJECT);
const canCreate = canCreateNamespace || canCreateProject;
const createProjectMessage = useSelector(({ UI }: RootState) => UI.get('createProjectMessage'));
const createNamespaceOrProjectModal = useCreateNamespaceOrProjectModal();
const onClickCreate = () =>
createNamespaceOrProjectModal({
onSubmit:
perspective !== 'admin'
? (project: K8sResourceKind) => {
setActiveNamespace(project.metadata?.name);
}
: undefined,
});

const primaryActions = canCreate
? [
<Button key="create-project-action" variant={ButtonVariant.primary} onClick={onClickCreate}>
{t('public~Create a new Project')}
</Button>,
]
: [];

return (
<>
{canCreate ? (
<p>
{t(
'public~OpenShift helps you quickly develop, host, and scale applications. To get started, create a project for your application.',
)}
</p>
) : (
<p>
{t(
"public~OpenShift helps you quickly develop, host, and scale applications. To get started, you'll need a project. Currently, you can't create or access any projects.",
)}
{!createProjectMessage && (
<>&nbsp;{t("public~You'll need to contact a cluster administrator for help.")}</>
)}
</p>
)}
{createProjectMessage && (
<p className="co-pre-line">
<LinkifyExternal>{createProjectMessage}</LinkifyExternal>
</p>
)}
const secondaryActions = [
<Button
key="download-cli-tools"
variant={ButtonVariant.link}
component="a"
href="/command-line-tools"
>
{t('public~Download command-line tools')}
</Button>,
<Button
key="visit-docs"
variant={ButtonVariant.link}
component="a"
href={openshiftHelpBase}
target="_blank"
rel="noopener noreferrer"
icon={<ExternalLinkAltIcon />}
iconPosition="end"
>
{t('public~View documentation')}
</Button>,
];
return (
<ConsoleEmptyState
variant={EmptyStateVariant.xl}
icon={ClusterIcon}
title={t('public~Hello, world!')}
primaryActions={primaryActions}
secondaryActions={secondaryActions}
>
{canCreate ? (
<p>{t('public~To get started, create a project for your application.')}</p>
) : (
<p>
<Trans t={t} ns="public">
To learn more, visit the OpenShift{' '}
<ExternalLink href={openshiftHelpBase}>documentation</ExternalLink>.
</Trans>
{t(
"public~To get started, you'll need a project. Currently, you can't create or access any projects.",
)}
{!createProjectMessage &&
t("public~ You'll need to contact a cluster administrator for help.")}
</p>
<p>
<Trans t={t} ns="public">
Download the <Link to="/command-line-tools">command-line tools</Link>
</Trans>
)}
{createProjectMessage && (
<p className="co-pre-line">
<LinkifyExternal>{createProjectMessage}</LinkifyExternal>
</p>
{canCreate ? (
<Button
variant="link"
onClick={() =>
createNamespaceOrProjectModal({
onSubmit:
perspective !== 'admin'
? (project: K8sResourceKind) => {
setActiveNamespace(project.metadata?.name);
}
: undefined,
})
}
>
{t('public~Create a new project')}
</Button>
) : null}
</>
);
},
);
)}
</ConsoleEmptyState>
);
};

type WithStartGuide = <P>(
WrappedComponent: React.ComponentType<P & WithStartGuideProps>,
disable?: boolean,
) => React.ComponentType<P>;

export const withStartGuide: WithStartGuide = (WrappedComponent, disable = true) =>
connectToFlags<any>(
FLAGS.SHOW_OPENSHIFT_START_GUIDE,
FLAGS.CAN_CREATE_NS,
FLAGS.CAN_CREATE_PROJECT,
)(({ flags, ...rest }: any) => {
const { kindObj } = rest;
const kind = _.get(kindObj, 'kind', rest.kind);
const { t } = useTranslation();
export const withStartGuide: WithStartGuide = (WrappedComponent, disable = true) => (
props: any,
) => {
const showOpenshiftStartGuide = useFlag(FLAGS.SHOW_OPENSHIFT_START_GUIDE);
const { kindObj } = props;
const kind = _.get(kindObj, 'kind', props.kind);

// The start guide does not need to be shown on the Projects list page.
if (kind === ProjectModel.kind) {
return <WrappedComponent {...rest} />;
}
// The start guide does not need to be shown on the Projects list page.
if (kind === ProjectModel.kind || !showOpenshiftStartGuide) {
return <WrappedComponent {...props} />;
}

if (flags[FLAGS.SHOW_OPENSHIFT_START_GUIDE]) {
return (
<>
<Hint className="pf-v6-u-m-md">
<HintTitle>{t('public~Getting Started')}</HintTitle>
<HintBody>
<OpenShiftGettingStarted
canCreate={flags[FLAGS.CAN_CREATE_NS] || flags[FLAGS.CAN_CREATE_PROJECT]}
/>
</HintBody>
</Hint>
{!disable || (rest.kindObj && !rest.kindObj.namespaced) ? (
<WrappedComponent {...rest} noProjectsAvailable />
) : (
<Disabled>
<WrappedComponent {...rest} noProjectsAvailable />
</Disabled>
)}
</>
);
}
return <WrappedComponent {...rest} />;
});
return (
<>
<OpenShiftGettingStarted />
<Divider />
{!disable || (props.kindObj && !props.kindObj.namespaced) ? (
<WrappedComponent {...props} noProjectsAvailable />
) : (
<Disabled>
<WrappedComponent {...props} noProjectsAvailable />
</Disabled>
)}
</>
);
};

type OpenShiftGettingStartedProps = {
canCreate: boolean;
createProjectMessage: string;
title?: string;
};

export type WithStartGuideProps = {
Expand Down
14 changes: 6 additions & 8 deletions frontend/public/locales/en/public.json
Original file line number Diff line number Diff line change
Expand Up @@ -1077,7 +1077,6 @@
"No results match the filter criteria.": "No results match the filter criteria.",
"Namespaces": "Namespaces",
"Projects": "Projects",
"Welcome to OpenShift": "Welcome to OpenShift",
"No matching Projects": "No matching Projects",
"by name or display name": "by name or display name",
"Project": "Project",
Expand Down Expand Up @@ -1408,13 +1407,12 @@
"Show YAML": "Show YAML",
"Samples": "Samples",
"Snippets": "Snippets",
"OpenShift helps you quickly develop, host, and scale applications. To get started, create a project for your application.": "OpenShift helps you quickly develop, host, and scale applications. To get started, create a project for your application.",
"OpenShift helps you quickly develop, host, and scale applications. To get started, you'll need a project. Currently, you can't create or access any projects.": "OpenShift helps you quickly develop, host, and scale applications. To get started, you'll need a project. Currently, you can't create or access any projects.",
"You'll need to contact a cluster administrator for help.": "You'll need to contact a cluster administrator for help.",
"To learn more, visit the OpenShift <2>documentation</2>.": "To learn more, visit the OpenShift <2>documentation</2>.",
"Download the <1>command-line tools</1>": "Download the <1>command-line tools</1>",
"Create a new project": "Create a new project",
"Getting Started": "Getting Started",
"Create a new Project": "Create a new Project",
"Download command-line tools": "Download command-line tools",
"Hello, world!": "Hello, world!",
"To get started, create a project for your application.": "To get started, create a project for your application.",
"To get started, you'll need a project. Currently, you can't create or access any projects.": "To get started, you'll need a project. Currently, you can't create or access any projects.",
" You'll need to contact a cluster administrator for help.": " You'll need to contact a cluster administrator for help.",
"StatefulSet details": "StatefulSet details",
"StatefulSets": "StatefulSets",
"Retain": "Retain",
Expand Down
4 changes: 0 additions & 4 deletions frontend/public/reducers/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,6 @@ export default (state: UIState, action: UIAction): UIState => {
return state;
};

export const createProjectMessageStateToProps = ({ UI }: RootState) => {
return { createProjectMessage: UI.get('createProjectMessage') as string };
};

export const userStateToProps = (state: RootState) => {
return { user: getUser(state) };
};
Expand Down