diff --git a/src/plugins/workspace/common/constants.ts b/src/plugins/workspace/common/constants.ts
index 036aa4ef7ce6..ef0821c88619 100644
--- a/src/plugins/workspace/common/constants.ts
+++ b/src/plugins/workspace/common/constants.ts
@@ -3,9 +3,6 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { i18n } from '@osd/i18n';
-import { AppCategory } from '../../../core/types';
-
export const WORKSPACE_CREATE_APP_ID = 'workspace_create';
export const WORKSPACE_LIST_APP_ID = 'workspace_list';
export const WORKSPACE_UPDATE_APP_ID = 'workspace_update';
@@ -22,12 +19,3 @@ export const PATHS = {
export const WORKSPACE_OP_TYPE_CREATE = 'create';
export const WORKSPACE_OP_TYPE_UPDATE = 'update';
export const WORKSPACE_SAVED_OBJECTS_CLIENT_WRAPPER_ID = 'workspace';
-
-export const WORKSPACE_NAV_CATEGORY: AppCategory = {
- id: 'workspace',
- label: i18n.translate('core.ui.workspaceNavList.label', {
- defaultMessage: 'Workspaces',
- }),
- euiIconType: 'folderClosed',
- order: 2000,
-};
diff --git a/src/plugins/workspace/public/components/workspace_creator/workspace_bottom_bar.tsx b/src/plugins/workspace/public/components/workspace_creator/workspace_bottom_bar.tsx
new file mode 100644
index 000000000000..79f1f92c8685
--- /dev/null
+++ b/src/plugins/workspace/public/components/workspace_creator/workspace_bottom_bar.tsx
@@ -0,0 +1,105 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {
+ EuiBottomBar,
+ EuiButton,
+ EuiButtonEmpty,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiSpacer,
+ EuiText,
+} from '@elastic/eui';
+import { i18n } from '@osd/i18n';
+import React, { useState } from 'react';
+import { ApplicationStart } from 'opensearch-dashboards/public';
+import { WORKSPACE_OP_TYPE_CREATE, WORKSPACE_OP_TYPE_UPDATE } from '../../../common/constants';
+import { WorkspaceCancelModal } from './workspace_cancel_modal';
+
+interface WorkspaceBottomBarProps {
+ formId: string;
+ opType?: string;
+ numberOfErrors: number;
+ application: ApplicationStart;
+}
+
+// Number of saved changes will be implemented in workspace update page PR
+export const WorkspaceBottomBar = ({
+ formId,
+ opType,
+ numberOfErrors,
+ application,
+}: WorkspaceBottomBarProps) => {
+ const [isCancelModalVisible, setIsCancelModalVisible] = useState(false);
+ const closeCancelModal = () => setIsCancelModalVisible(false);
+ const showCancelModal = () => setIsCancelModalVisible(true);
+
+ return (
+
+
+
+
+
+
+
+ {opType === WORKSPACE_OP_TYPE_UPDATE ? (
+
+ {i18n.translate('workspace.form.bottomBar.unsavedChanges', {
+ defaultMessage: '1 Unsaved change(s)',
+ })}
+
+ ) : (
+
+ {i18n.translate('workspace.form.bottomBar.errors', {
+ defaultMessage: `${numberOfErrors} Error(s)`,
+ })}
+
+ )}
+
+
+
+
+
+ {i18n.translate('workspace.form.bottomBar.cancel', {
+ defaultMessage: 'Cancel',
+ })}
+
+
+ {opType === WORKSPACE_OP_TYPE_CREATE && (
+
+ {i18n.translate('workspace.form.bottomBar.createWorkspace', {
+ defaultMessage: 'Create workspace',
+ })}
+
+ )}
+ {opType === WORKSPACE_OP_TYPE_UPDATE && (
+
+ {i18n.translate('workspace.form.bottomBar.saveChanges', {
+ defaultMessage: 'Save changes',
+ })}
+
+ )}
+
+
+
+
+
+
+ );
+};
diff --git a/src/plugins/workspace/public/components/workspace_creator/workspace_cancel_modal.tsx b/src/plugins/workspace/public/components/workspace_creator/workspace_cancel_modal.tsx
new file mode 100644
index 000000000000..040e46f9ddfc
--- /dev/null
+++ b/src/plugins/workspace/public/components/workspace_creator/workspace_cancel_modal.tsx
@@ -0,0 +1,49 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React from 'react';
+import { i18n } from '@osd/i18n';
+import { EuiConfirmModal } from '@elastic/eui';
+import { ApplicationStart } from 'opensearch-dashboards/public';
+import { WORKSPACE_LIST_APP_ID } from '../../../common/constants';
+
+interface WorkspaceCancelModalProps {
+ visible: boolean;
+ application: ApplicationStart;
+ closeCancelModal: () => void;
+}
+
+export const WorkspaceCancelModal = ({
+ application,
+ visible,
+ closeCancelModal,
+}: WorkspaceCancelModalProps) => {
+ if (!visible) {
+ return null;
+ }
+
+ return (
+ application?.navigateToApp(WORKSPACE_LIST_APP_ID)}
+ cancelButtonText={i18n.translate('workspace.form.cancelButtonText.', {
+ defaultMessage: 'Continue editing',
+ })}
+ confirmButtonText={i18n.translate('workspace.form.confirmButtonText.', {
+ defaultMessage: 'Discard changes',
+ })}
+ buttonColor="danger"
+ defaultFocusedButton="confirm"
+ >
+ {i18n.translate('workspace.form.cancelModal.body', {
+ defaultMessage: 'This will discard all changes. Are you sure?',
+ })}
+
+ );
+};
diff --git a/src/plugins/workspace/public/components/workspace_creator/workspace_creator.test.tsx b/src/plugins/workspace/public/components/workspace_creator/workspace_creator.test.tsx
new file mode 100644
index 000000000000..73633950d6b0
--- /dev/null
+++ b/src/plugins/workspace/public/components/workspace_creator/workspace_creator.test.tsx
@@ -0,0 +1,244 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React from 'react';
+import { PublicAppInfo } from 'opensearch-dashboards/public';
+import { fireEvent, render, waitFor } from '@testing-library/react';
+import { BehaviorSubject } from 'rxjs';
+import { WorkspaceCreator as WorkspaceCreatorComponent } from './workspace_creator';
+import { coreMock } from '../../../../../core/public/mocks';
+import { createOpenSearchDashboardsReactContext } from '../../../../opensearch_dashboards_react/public';
+
+const workspaceClientCreate = jest
+ .fn()
+ .mockReturnValue({ result: { id: 'successResult' }, success: true });
+
+const navigateToApp = jest.fn();
+const notificationToastsAddSuccess = jest.fn();
+const notificationToastsAddDanger = jest.fn();
+const PublicAPPInfoMap = new Map([
+ ['app1', { id: 'app1', title: 'app1' }],
+ ['app2', { id: 'app2', title: 'app2', category: { id: 'category1', label: 'category1' } }],
+ ['app3', { id: 'app3', category: { id: 'category1', label: 'category1' } }],
+ ['app4', { id: 'app4', category: { id: 'category2', label: 'category2' } }],
+ ['app5', { id: 'app5', category: { id: 'category2', label: 'category2' } }],
+]);
+
+const mockCoreStart = coreMock.createStart();
+
+const WorkspaceCreator = (props: any) => {
+ const { Provider } = createOpenSearchDashboardsReactContext({
+ ...mockCoreStart,
+ ...{
+ application: {
+ ...mockCoreStart.application,
+ capabilities: {
+ ...mockCoreStart.application.capabilities,
+ workspaces: {
+ permissionEnabled: true,
+ },
+ },
+ navigateToApp,
+ getUrlForApp: jest.fn(),
+ applications$: new BehaviorSubject