diff --git a/frontend/src/__tests__/cypress/cypress/pages/projects.ts b/frontend/src/__tests__/cypress/cypress/pages/projects.ts
index 1a9b82ede5..1870fe8721 100644
--- a/frontend/src/__tests__/cypress/cypress/pages/projects.ts
+++ b/frontend/src/__tests__/cypress/cypress/pages/projects.ts
@@ -113,7 +113,7 @@ class ProjectListPage {
class CreateEditProjectModal extends Modal {
constructor(private edit = false) {
- super(`${edit ? 'Edit' : 'Create'} data science project`);
+ super(`${edit ? 'Edit' : 'Create'} project`);
}
findNameInput() {
diff --git a/frontend/src/concepts/design/utils.ts b/frontend/src/concepts/design/utils.ts
index eb53428ee6..8e4e91da87 100644
--- a/frontend/src/concepts/design/utils.ts
+++ b/frontend/src/concepts/design/utils.ts
@@ -10,7 +10,7 @@ import deployingModelsImg from '~/images/UI_icon-Red_Hat-Server_upload-RGB.svg';
import dataConnectionImg from '~/images/UI_icon-Red_Hat-Connected-RGB.svg';
import userImg from '~/images/UI_icon-Red_Hat-User-RGB.svg';
import groupImg from '~/images/UI_icon-Red_Hat-Shared_workspace-RGB.svg';
-import projectEmptyStateImg from '~/images/empty-state-projects-overview.svg';
+import projectEmptyStateImg from '~/images/empty-state-project-overview.svg';
import notebookEmptyStateImg from '~/images/empty-state-notebooks.svg';
import pipelineEmptyStateImg from '~/images/empty-state-pipelines.svg';
import clusterStorageEmptyStateImg from '~/images/empty-state-cluster-storage.svg';
diff --git a/frontend/src/images/empty-state-projects-overview.svg b/frontend/src/images/empty-state-project-overview.svg
similarity index 100%
rename from frontend/src/images/empty-state-projects-overview.svg
rename to frontend/src/images/empty-state-project-overview.svg
diff --git a/frontend/src/images/empty-state-projects-color.svg b/frontend/src/images/empty-state-projects-color.svg
new file mode 100644
index 0000000000..04f4244695
--- /dev/null
+++ b/frontend/src/images/empty-state-projects-color.svg
@@ -0,0 +1,275 @@
+
+
\ No newline at end of file
diff --git a/frontend/src/pages/projects/screens/projects/EmptyProjects.tsx b/frontend/src/pages/projects/screens/projects/EmptyProjects.tsx
index 37a2dba510..f5724c6381 100644
--- a/frontend/src/pages/projects/screens/projects/EmptyProjects.tsx
+++ b/frontend/src/pages/projects/screens/projects/EmptyProjects.tsx
@@ -1,18 +1,20 @@
import * as React from 'react';
import {
- ButtonVariant,
EmptyState,
EmptyStateBody,
EmptyStateIcon,
- EmptyStateActions,
EmptyStateHeader,
EmptyStateFooter,
+ Popover,
+ Button,
+ Icon,
+ TextContent,
+ TextList,
+ TextListItem,
} from '@patternfly/react-core';
-import { CubesIcon } from '@patternfly/react-icons';
import { useNavigate } from 'react-router-dom';
-import { ODH_PRODUCT_NAME } from '~/utilities/const';
-import LaunchJupyterButton from '~/pages/projects/screens/projects/LaunchJupyterButton';
-import { useCheckJupyterEnabled } from '~/utilities/notebookControllerUtils';
+import { OutlinedQuestionCircleIcon } from '@patternfly/react-icons';
+import projectsEmptyStateImg from '~/images/empty-state-projects-color.svg';
import NewProjectButton from './NewProjectButton';
type EmptyProjectsProps = {
@@ -21,23 +23,24 @@ type EmptyProjectsProps = {
const EmptyProjects: React.FC = ({ allowCreate }) => {
const navigate = useNavigate();
- const isJupyterEnabled = useCheckJupyterEnabled();
+
return (
-
+
}
+ titleText={allowCreate ? 'Start by creating your project' : 'Start by requesting a project'}
+ icon={
+ (
+
+ )}
+ />
+ }
headingLevel="h2"
/>
-
- {allowCreate
- ? `To get started, create a data science project${
- isJupyterEnabled ? ' or launch a notebook with Jupyter' : ''
- }.`
- : `To get started, ask your ${ODH_PRODUCT_NAME} admin for a data science project${
- isJupyterEnabled ? ' or launch a notebook with Jupyter' : ''
- }.`}
+
+ Projects allow you and your team to organize and collaborate on resources within separate
+ namespaces.{!allowCreate ? ' To request a project, contact your administrator.' : ''}
{allowCreate ? (
@@ -45,12 +48,39 @@ const EmptyProjects: React.FC = ({ allowCreate }) => {
navigate(`/projects/${projectName}`)}
/>
-
-
-
>
) : (
-
+
+
+ The person who gave you your username
+
+ Someone in your IT department or Help desk (at a company or school)
+
+
+ The person who manages your email service or web site (in a small business or
+ club)
+
+
+
+ }
+ >
+
+
)}
diff --git a/frontend/src/pages/projects/screens/projects/LaunchJupyterButton.tsx b/frontend/src/pages/projects/screens/projects/LaunchJupyterButton.tsx
index 4800582867..c1ec904725 100644
--- a/frontend/src/pages/projects/screens/projects/LaunchJupyterButton.tsx
+++ b/frontend/src/pages/projects/screens/projects/LaunchJupyterButton.tsx
@@ -1,13 +1,9 @@
import * as React from 'react';
-import { Button, ButtonVariant } from '@patternfly/react-core';
+import { Button, ButtonVariant, Tooltip } from '@patternfly/react-core';
import { useNavigate } from 'react-router-dom';
import { useCheckJupyterEnabled } from '~/utilities/notebookControllerUtils';
-type LaunchJupyterButtonProps = {
- variant: ButtonVariant;
-};
-
-const LaunchJupyterButton: React.FC = ({ variant }) => {
+const LaunchJupyterButton: React.FC = () => {
const navigate = useNavigate();
const isJupyterEnabled = useCheckJupyterEnabled();
@@ -16,16 +12,22 @@ const LaunchJupyterButton: React.FC = ({ variant }) =>
}
return (
-
+
+
);
};
diff --git a/frontend/src/pages/projects/screens/projects/ManageProjectModal.tsx b/frontend/src/pages/projects/screens/projects/ManageProjectModal.tsx
index c7cb62fe36..e994567797 100644
--- a/frontend/src/pages/projects/screens/projects/ManageProjectModal.tsx
+++ b/frontend/src/pages/projects/screens/projects/ManageProjectModal.tsx
@@ -92,7 +92,7 @@ const ManageProjectModal: React.FC = ({
return (
onBeforeClose()}
diff --git a/frontend/src/pages/projects/screens/projects/NewProjectButton.tsx b/frontend/src/pages/projects/screens/projects/NewProjectButton.tsx
index 72f5d5a79e..1d3697dd40 100644
--- a/frontend/src/pages/projects/screens/projects/NewProjectButton.tsx
+++ b/frontend/src/pages/projects/screens/projects/NewProjectButton.tsx
@@ -17,7 +17,7 @@ const NewProjectButton: React.FC = ({ closeOnCreate, onPr
variant="primary"
onClick={() => setOpen(true)}
>
- Create data science project
+ Create project
= ({ allowCreate }) => {
- const { dashboardConfig } = useAppContext();
const { projects } = React.useContext(ProjectsContext);
const navigate = useNavigate();
const [searchType, setSearchType] = React.useState(SearchType.NAME);
@@ -94,11 +91,6 @@ const ProjectListView: React.FC = ({ allowCreate }) => {
- {dashboardConfig.spec.notebookController?.enabled && (
-
-
-
- )}
{allowCreate && (
{
+ const { dashboardConfig } = useAppContext();
const { projects } = React.useContext(ProjectsContext);
const [allowCreate, rbacLoaded] = useAccessReview(accessReviewResource);
return (
}
+ headerAction={
+ dashboardConfig.spec.notebookController?.enabled ? : undefined
+ }
description={
rbacLoaded
? `View your existing projects${allowCreate ? ' or create new projects' : ''}.`
diff --git a/frontend/src/pages/projects/screens/projects/__tests__/EmptyProjects.spec.tsx b/frontend/src/pages/projects/screens/projects/__tests__/EmptyProjects.spec.tsx
new file mode 100644
index 0000000000..4397c6b49c
--- /dev/null
+++ b/frontend/src/pages/projects/screens/projects/__tests__/EmptyProjects.spec.tsx
@@ -0,0 +1,53 @@
+import React from 'react';
+import { act, render, screen } from '@testing-library/react';
+import '@testing-library/jest-dom';
+import EmptyProjects from '~/pages/projects/screens/projects/EmptyProjects';
+import { useUser } from '~/redux/selectors';
+
+jest.mock('~/app/AppContext', () => ({
+ __esModule: true,
+ useAppContext: jest.fn(),
+}));
+
+jest.mock('~/redux/selectors', () => ({
+ ...jest.requireActual('~/redux/selectors'),
+ useUser: jest.fn(),
+ useClusterInfo: jest.fn(),
+}));
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useNavigate: jest.fn(),
+}));
+
+const useUserMock = jest.mocked(useUser);
+useUserMock.mockReturnValue({
+ username: 'test-user',
+ isAdmin: false,
+ isAllowed: true,
+ userLoading: false,
+ userError: null,
+});
+
+describe('EmptyProjects', () => {
+ it('should show the create project button when allowed to create projects', async () => {
+ render();
+ const createProject = await screen.findByTestId('create-data-science-project');
+ expect(createProject).toBeEnabled();
+ const bodyText = await screen.findByTestId('projects-empty-body-text');
+ expect(bodyText).toHaveTextContent(
+ 'Projects allow you and your team to organize and collaborate on resources within separate namespaces.',
+ );
+ expect(bodyText).not.toHaveTextContent('To request a project, contact your administrator.');
+ });
+ it('should show the who is my admin help when not allowed to create projects', async () => {
+ render();
+ const bodyText = await screen.findByTestId('projects-empty-body-text');
+ expect(bodyText).toHaveTextContent(
+ 'Projects allow you and your team to organize and collaborate on resources within separate namespaces. To request a project, contact your administrator.',
+ );
+ const adminHelpButton = await screen.findByTestId('projects-empty-admin-help');
+ act(() => adminHelpButton.click());
+ const helpContent = await screen.findByTestId('projects-empty-admin-help-content');
+ expect(helpContent).toBeVisible();
+ });
+});